This commit is contained in:
@@ -2,7 +2,8 @@
|
|||||||
"name": "cpl",
|
"name": "cpl",
|
||||||
"projects": [
|
"projects": [
|
||||||
"src/cli/cpl.project.json",
|
"src/cli/cpl.project.json",
|
||||||
"src/core/cpl.project.json"
|
"src/core/cpl.project.json",
|
||||||
|
"test/cpl.project.json"
|
||||||
],
|
],
|
||||||
"defaultProject": "cpl-cli",
|
"defaultProject": "cpl-cli",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ TApp = TypeVar("TApp", bound=ApplicationABC)
|
|||||||
|
|
||||||
class ApplicationBuilder(Generic[TApp]):
|
class ApplicationBuilder(Generic[TApp]):
|
||||||
|
|
||||||
def __init__(self, app: Type[ApplicationABC]):
|
def __init__(self, app: Type[TApp]):
|
||||||
assert app is not None, "app must not be None"
|
assert app is not None, "app must not be None"
|
||||||
assert issubclass(app, ApplicationABC), "app must be an subclass of ApplicationABC or its subclass"
|
assert issubclass(app, ApplicationABC), "app must be an subclass of ApplicationABC or its subclass"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from cpl.dependency import get_provider
|
from cpl.core.property import classproperty
|
||||||
|
from cpl.dependency.context import get_provider, use_root_provider
|
||||||
|
from cpl.dependency.service_collection import ServiceCollection
|
||||||
from cpl.dependency.hosted.startup_task import StartupTask
|
from cpl.dependency.hosted.startup_task import StartupTask
|
||||||
|
|
||||||
|
|
||||||
@@ -9,6 +11,24 @@ class Host:
|
|||||||
_loop: asyncio.AbstractEventLoop | None = None
|
_loop: asyncio.AbstractEventLoop | None = None
|
||||||
_tasks: dict = {}
|
_tasks: dict = {}
|
||||||
|
|
||||||
|
_service_collection: ServiceCollection | None = None
|
||||||
|
|
||||||
|
@classproperty
|
||||||
|
def services(cls) -> ServiceCollection:
|
||||||
|
if cls._service_collection is None:
|
||||||
|
cls._service_collection = ServiceCollection()
|
||||||
|
|
||||||
|
return cls._service_collection
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_provider(cls):
|
||||||
|
provider = get_provider()
|
||||||
|
if provider is None:
|
||||||
|
provider = cls.services.build()
|
||||||
|
use_root_provider(provider)
|
||||||
|
|
||||||
|
return provider
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_loop(cls) -> asyncio.AbstractEventLoop:
|
def get_loop(cls) -> asyncio.AbstractEventLoop:
|
||||||
if cls._loop is None:
|
if cls._loop is None:
|
||||||
@@ -18,7 +38,7 @@ class Host:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_start_tasks(cls):
|
def run_start_tasks(cls):
|
||||||
provider = get_provider()
|
provider = cls.get_provider()
|
||||||
tasks = provider.get_services(StartupTask)
|
tasks = provider.get_services(StartupTask)
|
||||||
loop = cls.get_loop()
|
loop = cls.get_loop()
|
||||||
|
|
||||||
@@ -30,7 +50,7 @@ class Host:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_hosted_services(cls):
|
def run_hosted_services(cls):
|
||||||
provider = get_provider()
|
provider = cls.get_provider()
|
||||||
services = provider.get_hosted_services()
|
services = provider.get_hosted_services()
|
||||||
loop = cls.get_loop()
|
loop = cls.get_loop()
|
||||||
|
|
||||||
@@ -49,6 +69,10 @@ class Host:
|
|||||||
|
|
||||||
cls._tasks.clear()
|
cls._tasks.clear()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def wait_for_all(cls):
|
||||||
|
await asyncio.gather(*cls._tasks.values())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_app(cls, func: Callable, *args, **kwargs):
|
def run_app(cls, func: Callable, *args, **kwargs):
|
||||||
cls.run_start_tasks()
|
cls.run_start_tasks()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from pathlib import Path
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.structure import get_project_by_name_or_path
|
from cpl.cli.utils.structure import Structure
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@@ -12,8 +12,8 @@ from cpl.core.console import Console
|
|||||||
@click.argument("target", type=click.STRING, required=True)
|
@click.argument("target", type=click.STRING, required=True)
|
||||||
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
||||||
def add(reference: str, target: str, verbose: bool):
|
def add(reference: str, target: str, verbose: bool):
|
||||||
reference_project = get_project_by_name_or_path(reference)
|
reference_project = Structure.get_project_by_name_or_path(reference)
|
||||||
target_project = get_project_by_name_or_path(target)
|
target_project = Structure.get_project_by_name_or_path(target)
|
||||||
|
|
||||||
if reference_project.name == target_project.name:
|
if reference_project.name == target_project.name:
|
||||||
raise ValueError("Cannot add a project as a dependency to itself!")
|
raise ValueError("Cannot add a project as a dependency to itself!")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import click
|
|||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.pip import Pip
|
from cpl.cli.utils.pip import Pip
|
||||||
from cpl.cli.utils.structure import get_project_by_name_or_path
|
from cpl.cli.utils.structure import Structure
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ from cpl.core.console import Console
|
|||||||
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
||||||
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
||||||
def install(package: str, project: str, dev: bool, verbose: bool):
|
def install(package: str, project: str, dev: bool, verbose: bool):
|
||||||
project = get_project_by_name_or_path(project or "./")
|
project = Structure.get_project_by_name_or_path(project or "./")
|
||||||
|
|
||||||
if package is not None:
|
if package is not None:
|
||||||
Console.write_line(f"Installing {package} to '{project.name}':")
|
Console.write_line(f"Installing {package} to '{project.name}':")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from pathlib import Path
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.structure import get_project_by_name_or_path
|
from cpl.cli.utils.structure import Structure
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@ from cpl.core.console import Console
|
|||||||
@click.argument("reference", type=click.STRING, required=True)
|
@click.argument("reference", type=click.STRING, required=True)
|
||||||
@click.argument("target", type=click.STRING, required=True)
|
@click.argument("target", type=click.STRING, required=True)
|
||||||
def remove(reference: str, target: str):
|
def remove(reference: str, target: str):
|
||||||
reference_project = get_project_by_name_or_path(reference)
|
reference_project = Structure.get_project_by_name_or_path(reference)
|
||||||
target_project = get_project_by_name_or_path(target)
|
target_project = Structure.get_project_by_name_or_path(target)
|
||||||
|
|
||||||
if reference_project.name == target_project.name:
|
if reference_project.name == target_project.name:
|
||||||
raise ValueError("Cannot add a project as a dependency to itself!")
|
raise ValueError("Cannot add a project as a dependency to itself!")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import click
|
|||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.pip import Pip
|
from cpl.cli.utils.pip import Pip
|
||||||
from cpl.cli.utils.structure import get_project_by_name_or_path
|
from cpl.cli.utils.structure import Structure
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ def uninstall(package: str, project: str, dev: bool, verbose: bool):
|
|||||||
if package is None:
|
if package is None:
|
||||||
package = Console.read("Package name to uninstall: ").strip()
|
package = Console.read("Package name to uninstall: ").strip()
|
||||||
|
|
||||||
project = get_project_by_name_or_path(project or "./")
|
project = Structure.get_project_by_name_or_path(project or "./")
|
||||||
|
|
||||||
deps = project.dependencies if not dev else project.dev_dependencies
|
deps = project.dependencies if not dev else project.dev_dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import click
|
|||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.pip import Pip
|
from cpl.cli.utils.pip import Pip
|
||||||
from cpl.cli.utils.structure import get_project_by_name_or_path
|
from cpl.cli.utils.structure import Structure
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ from cpl.core.console import Console
|
|||||||
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
@click.option("--dev", is_flag=True, help="Include dev dependencies")
|
||||||
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
||||||
def update(package: str, project: str, dev: bool, verbose: bool):
|
def update(package: str, project: str, dev: bool, verbose: bool):
|
||||||
project = get_project_by_name_or_path(project or "./")
|
project = Structure.get_project_by_name_or_path(project or "./")
|
||||||
|
|
||||||
deps: dict = project.dependencies
|
deps: dict = project.dependencies
|
||||||
if dev:
|
if dev:
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ from pathlib import Path
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.model.project import Project
|
|
||||||
from cpl.cli.utils.structure import get_project_by_name_or_path
|
|
||||||
from cpl.cli.utils.venv import ensure_venv, get_venv_python
|
from cpl.cli.utils.venv import ensure_venv, get_venv_python
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
@@ -18,7 +16,9 @@ from cpl.core.console import Console
|
|||||||
@click.option("--skip-py-build", "-spb", is_flag=True, help="Skip toml generation and python build")
|
@click.option("--skip-py-build", "-spb", is_flag=True, help="Skip toml generation and python build")
|
||||||
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
||||||
def build(project: str, dist: str = None, skip_py_build: bool = None, verbose: bool = None):
|
def build(project: str, dist: str = None, skip_py_build: bool = None, verbose: bool = None):
|
||||||
project = get_project_by_name_or_path(project or "./")
|
from cpl.cli.utils.structure import Structure
|
||||||
|
|
||||||
|
project = Structure.get_project_by_name_or_path(project or "./")
|
||||||
venv = ensure_venv().absolute()
|
venv = ensure_venv().absolute()
|
||||||
dist_path = dist or Path(project.path).parent / "dist"
|
dist_path = dist or Path(project.path).parent / "dist"
|
||||||
|
|
||||||
@@ -38,9 +38,7 @@ def build(project: str, dist: str = None, skip_py_build: bool = None, verbose: b
|
|||||||
Console.write_line("\nDone!")
|
Console.write_line("\nDone!")
|
||||||
return
|
return
|
||||||
|
|
||||||
from cpl.cli.utils.structure import create_pyproject_toml
|
Structure.create_pyproject_toml(project, dist_path / project.name)
|
||||||
|
|
||||||
create_pyproject_toml(project, dist_path / project.name)
|
|
||||||
python = str(get_venv_python(venv))
|
python = str(get_venv_python(venv))
|
||||||
|
|
||||||
result = Console.spinner(
|
result = Console.spinner(
|
||||||
|
|||||||
@@ -5,19 +5,20 @@ from pathlib import Path
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.structure import get_project_by_name_or_path
|
from cpl.cli.utils.structure import Structure
|
||||||
from cpl.cli.utils.venv import get_venv_python, ensure_venv
|
from cpl.cli.utils.venv import get_venv_python, ensure_venv
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@cli.command("run", aliases=["r"])
|
@cli.command("run", aliases=["r"])
|
||||||
|
@click.argument("project", type=str)
|
||||||
@click.argument("args", nargs=-1)
|
@click.argument("args", nargs=-1)
|
||||||
@click.option("--project", "-p", type=str)
|
|
||||||
@click.option("--dev", "-d", is_flag=True, help="Use sources instead of build output")
|
@click.option("--dev", "-d", is_flag=True, help="Use sources instead of build output")
|
||||||
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
||||||
def run(project: str, args: list[str], dev: bool, verbose: bool):
|
def run(project: str, args: list[str], dev: bool, verbose: bool):
|
||||||
project = get_project_by_name_or_path(project or "./")
|
Console.error(project)
|
||||||
|
project = Structure.get_project_by_name_or_path(project or "./")
|
||||||
if project.main is None:
|
if project.main is None:
|
||||||
Console.error(f"Project {project.name} has no executable")
|
Console.error(f"Project {project.name} has no executable")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ import click
|
|||||||
|
|
||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.utils.live_server.live_server import LiveServer
|
from cpl.cli.utils.live_server.live_server import LiveServer
|
||||||
from cpl.cli.utils.structure import get_project_by_name_or_path
|
from cpl.cli.utils.structure import Structure
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@cli.command("start", aliases=["s"])
|
@cli.command("start", aliases=["s"])
|
||||||
|
@click.argument("project", type=str)
|
||||||
@click.argument("args", nargs=-1)
|
@click.argument("args", nargs=-1)
|
||||||
@click.option("--project", "-p", type=str)
|
|
||||||
@click.option("--dev", "-d", is_flag=True, help="Use sources instead of build output")
|
@click.option("--dev", "-d", is_flag=True, help="Use sources instead of build output")
|
||||||
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
||||||
def start(project: str, args: list[str], dev: bool, verbose: bool):
|
def start(project: str, args: list[str], dev: bool, verbose: bool):
|
||||||
project = get_project_by_name_or_path(project or "./")
|
project = Structure.get_project_by_name_or_path(project or "./")
|
||||||
if project.main is None:
|
if project.main is None:
|
||||||
Console.error(f"Project {project.name} has no executable")
|
Console.error(f"Project {project.name} has no executable")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from cpl.cli import cli as clim
|
|||||||
from cpl.cli.cli import cli
|
from cpl.cli.cli import cli
|
||||||
from cpl.cli.model.project import Project
|
from cpl.cli.model.project import Project
|
||||||
from cpl.cli.model.workspace import Workspace
|
from cpl.cli.model.workspace import Workspace
|
||||||
from cpl.cli.utils.structure import get_project_by_name_or_path
|
from cpl.cli.utils.structure import Structure
|
||||||
from cpl.cli.utils.template_collector import TemplateCollector
|
from cpl.cli.utils.template_collector import TemplateCollector
|
||||||
from cpl.cli.utils.template_renderer import TemplateRenderer
|
from cpl.cli.utils.template_renderer import TemplateRenderer
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.core.configuration import Configuration
|
||||||
@@ -29,7 +29,7 @@ def generate(schematic: str, name: str, verbose: bool) -> None:
|
|||||||
|
|
||||||
project = None
|
project = None
|
||||||
try:
|
try:
|
||||||
project = get_project_by_name_or_path("./")
|
project = Structure.get_project_by_name_or_path("./")
|
||||||
if verbose:
|
if verbose:
|
||||||
Console.write_line("project found, collecting templates...")
|
Console.write_line("project found, collecting templates...")
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,8 @@ from pathlib import Path
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
from cpl.cli.const import PROJECT_TYPES, PROJECT_TYPES_SHORT
|
from cpl.cli.const import PROJECT_TYPES, PROJECT_TYPES_SHORT
|
||||||
from cpl.cli.model.project import Project
|
|
||||||
from cpl.cli.model.workspace import Workspace
|
|
||||||
from cpl.cli.utils.prompt import SmartChoice
|
from cpl.cli.utils.prompt import SmartChoice
|
||||||
|
from cpl.cli.utils.structure import Structure
|
||||||
from cpl.core.console import Console
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
@@ -25,62 +24,10 @@ def init(target: str, name: str):
|
|||||||
target = [pt for pt in PROJECT_TYPES if pt.startswith(target)][0]
|
target = [pt for pt in PROJECT_TYPES if pt.startswith(target)][0]
|
||||||
|
|
||||||
if target in ["workspace", "ws"]:
|
if target in ["workspace", "ws"]:
|
||||||
_init_workspace(name or click.prompt("Workspace name", default="my-workspace"))
|
Structure.init_workspace("./", name or click.prompt("Workspace name", default="my-workspace"))
|
||||||
elif target in PROJECT_TYPES:
|
elif target in PROJECT_TYPES:
|
||||||
_init_project(name or click.prompt("Project name", default=f"my-{target}"), target)
|
workspace = Structure.find_workspace_in_path(Path(name).parent)
|
||||||
|
Structure.init_project("./", name or click.prompt("Project name", default=f"my-{target}"), target, workspace)
|
||||||
else:
|
else:
|
||||||
Console.error(f"Unknown target '{target}'")
|
Console.error(f"Unknown target '{target}'")
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
|
||||||
def _init_workspace(name: str):
|
|
||||||
path = Path("cpl.workspace.json")
|
|
||||||
|
|
||||||
if path.exists():
|
|
||||||
Console.write_line("workspace.json already exists.")
|
|
||||||
return
|
|
||||||
|
|
||||||
data = {
|
|
||||||
"name": name,
|
|
||||||
"projects": [],
|
|
||||||
"defaultProject": None,
|
|
||||||
"scripts": {},
|
|
||||||
}
|
|
||||||
workspace = Workspace.new("./", name)
|
|
||||||
workspace.save()
|
|
||||||
|
|
||||||
Console.write_line(f"Created workspace '{name}'")
|
|
||||||
|
|
||||||
|
|
||||||
def _init_project(name: str, project_type: str):
|
|
||||||
project = Project.new("./", name, project_type)
|
|
||||||
|
|
||||||
path = Path("cpl.project.json")
|
|
||||||
if path.exists():
|
|
||||||
Console.write_line("cpl.project.json already exists.")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not Path("src").exists():
|
|
||||||
project.directory = click.prompt(
|
|
||||||
"Project directory", type=click.Path(exists=True, file_okay=False), default="src"
|
|
||||||
)
|
|
||||||
|
|
||||||
if project_type in ["console", "web", "service"]:
|
|
||||||
project.main = click.prompt(
|
|
||||||
"Main executable", type=click.Path(exists=True, dir_okay=False), default="src/main.py"
|
|
||||||
)
|
|
||||||
|
|
||||||
project.save()
|
|
||||||
|
|
||||||
workspace_file = Path("../cpl.workspace.json")
|
|
||||||
if workspace_file.exists():
|
|
||||||
workspace = Workspace.from_file(workspace_file)
|
|
||||||
|
|
||||||
rel_path = str(Path.cwd().relative_to(workspace_file.parent))
|
|
||||||
if rel_path not in workspace.projects:
|
|
||||||
workspace.projects.append(rel_path)
|
|
||||||
workspace.save()
|
|
||||||
|
|
||||||
Console.write_line(f"Registered '{name}' in workspace.json")
|
|
||||||
|
|
||||||
Console.write_line(f"Created {project_type} project '{name}'")
|
|
||||||
|
|||||||
64
src/cli/cpl/cli/command/structure/new.py
Normal file
64
src/cli/cpl/cli/command/structure/new.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import click
|
||||||
|
from cpl.cli import cli as clim
|
||||||
|
from cpl.cli.cli import cli
|
||||||
|
from cpl.cli.const import PROJECT_TYPES, PROJECT_TYPES_SHORT
|
||||||
|
from cpl.cli.model.workspace import Workspace
|
||||||
|
from cpl.cli.utils.structure import Structure
|
||||||
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
|
@cli.command("new", aliases=["n"])
|
||||||
|
@click.argument("type", type=click.STRING, required=True)
|
||||||
|
@click.argument("name", type=click.STRING, required=True)
|
||||||
|
@click.option("--name", "in_name", type=click.STRING, help="Name of the workspace or project to create.")
|
||||||
|
@click.option(
|
||||||
|
"--project",
|
||||||
|
"-p",
|
||||||
|
nargs=2,
|
||||||
|
metavar="<type> <name>",
|
||||||
|
help="Optional: when creating a workspace, also create a project with the given name and type.",
|
||||||
|
)
|
||||||
|
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
|
||||||
|
def new(type: str, name: str, in_name: str | None, project: list[str] | None, verbose: bool) -> None:
|
||||||
|
workspace_created = False
|
||||||
|
path = Path(name).parent
|
||||||
|
project_name = in_name or Path(name).stem
|
||||||
|
|
||||||
|
if type in ["workspace", "ws"]:
|
||||||
|
Structure.init_workspace(name, project_name)
|
||||||
|
workspace = Workspace.from_file(Path(name) / "cpl.workspace.json")
|
||||||
|
workspace_created = True
|
||||||
|
|
||||||
|
if len(project) == 2:
|
||||||
|
type = project[0]
|
||||||
|
if type not in PROJECT_TYPES + PROJECT_TYPES_SHORT:
|
||||||
|
raise ValueError(f"Unknown project type '{type}'")
|
||||||
|
|
||||||
|
path = Path(workspace.path).parent / Path(project[1]).parent
|
||||||
|
project_name = Path(project[1]).stem
|
||||||
|
|
||||||
|
workspace = Structure.find_workspace_in_path(path)
|
||||||
|
if workspace is None:
|
||||||
|
Console.error("No workspace found. Please run 'cpl init workspace' first.")
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
if project_name in workspace.project_names:
|
||||||
|
Console.error(f"Project '{project_name}' already exists in the workspace")
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
Console.write_line(f"Creating project '{path/project_name}'...")
|
||||||
|
|
||||||
|
project_types = os.listdir(Path(clim.__file__).parent / ".cpl" / "new")
|
||||||
|
project_types.extend(set(x[0] for x in PROJECT_TYPES))
|
||||||
|
|
||||||
|
if type not in project_types:
|
||||||
|
raise ValueError(f"Unsupported project type '{type}'")
|
||||||
|
|
||||||
|
Structure.create_project(path, type, project_name, workspace, verbose)
|
||||||
|
if workspace_created:
|
||||||
|
workspace.default_project = project_name
|
||||||
|
workspace.save()
|
||||||
@@ -12,6 +12,7 @@ from cpl.cli.command.project.run import run
|
|||||||
from cpl.cli.command.project.start import start
|
from cpl.cli.command.project.start import start
|
||||||
from cpl.cli.command.structure.generate import generate
|
from cpl.cli.command.structure.generate import generate
|
||||||
from cpl.cli.command.structure.init import init
|
from cpl.cli.command.structure.init import init
|
||||||
|
from cpl.cli.command.structure.new import new
|
||||||
from cpl.cli.command.version import version
|
from cpl.cli.command.version import version
|
||||||
from cpl.cli.model.workspace import Workspace
|
from cpl.cli.model.workspace import Workspace
|
||||||
from cpl.cli.utils.custom_command import script_command
|
from cpl.cli.utils.custom_command import script_command
|
||||||
@@ -36,7 +37,7 @@ def _load_scripts():
|
|||||||
if ws is None:
|
if ws is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
Configuration.set("workspace", Workspace.from_file(p))
|
Configuration.set("workspace", ws)
|
||||||
return ws.scripts
|
return ws.scripts
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
@@ -54,7 +55,7 @@ def configure():
|
|||||||
|
|
||||||
# structure
|
# structure
|
||||||
cli.add_command(init)
|
cli.add_command(init)
|
||||||
# cli.add_command(new)
|
cli.add_command(new)
|
||||||
cli.add_command(generate)
|
cli.add_command(generate)
|
||||||
|
|
||||||
# packaging
|
# packaging
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ from cpl.cli.model.cpl_sub_structure_model import CPLSubStructureModel
|
|||||||
|
|
||||||
class Build(CPLSubStructureModel):
|
class Build(CPLSubStructureModel):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def new(include: list[str], exclude: list[str]) -> "Build":
|
||||||
|
return Build(include, exclude)
|
||||||
|
|
||||||
def __init__(self, include: list[str], exclude: list[str]):
|
def __init__(self, include: list[str], exclude: list[str]):
|
||||||
CPLSubStructureModel.__init__(self)
|
CPLSubStructureModel.__init__(self)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
from inspect import isclass
|
from inspect import isclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Type, TypeVar
|
from typing import Any, Dict, List, Optional, Type, TypeVar
|
||||||
@@ -81,6 +82,9 @@ class CPLStructureModel:
|
|||||||
if not self._path:
|
if not self._path:
|
||||||
raise ValueError("Cannot save model without a path.")
|
raise ValueError("Cannot save model without a path.")
|
||||||
|
|
||||||
|
if not Path(self._path).exists():
|
||||||
|
os.makedirs(Path(self._path).parent, exist_ok=True)
|
||||||
|
|
||||||
with open(self._path, "w", encoding="utf-8") as f:
|
with open(self._path, "w", encoding="utf-8") as f:
|
||||||
json.dump(self.to_json(), f, indent=2)
|
json.dump(self.to_json(), f, indent=2)
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ class Project(CPLStructureModel):
|
|||||||
{},
|
{},
|
||||||
[],
|
[],
|
||||||
None,
|
None,
|
||||||
"src",
|
"./",
|
||||||
|
Build.new([], []),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -192,6 +193,7 @@ class Project(CPLStructureModel):
|
|||||||
def _collect_files(self, rel_dir: Path) -> List[Path]:
|
def _collect_files(self, rel_dir: Path) -> List[Path]:
|
||||||
files: List[Path] = []
|
files: List[Path] = []
|
||||||
exclude_patterns = [p.strip() for p in self._build.exclude or []]
|
exclude_patterns = [p.strip() for p in self._build.exclude or []]
|
||||||
|
exclude_patterns.append("cpl.*.json")
|
||||||
|
|
||||||
for root, dirnames, filenames in os.walk(rel_dir, topdown=True):
|
for root, dirnames, filenames in os.walk(rel_dir, topdown=True):
|
||||||
root_path = Path(root)
|
root_path = Path(root)
|
||||||
|
|||||||
@@ -1,43 +1,31 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
import textwrap
|
import textwrap
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import click
|
||||||
|
|
||||||
|
import cpl.cli as cli
|
||||||
from cpl.cli.model.project import Project
|
from cpl.cli.model.project import Project
|
||||||
from cpl.core.configuration import Configuration
|
from cpl.cli.model.workspace import Workspace
|
||||||
|
from cpl.cli.utils.template_renderer import TemplateRenderer
|
||||||
|
from cpl.core.console import Console
|
||||||
|
|
||||||
|
|
||||||
def get_project_by_name_or_path(project: str) -> Project:
|
class Structure:
|
||||||
if project is None:
|
@staticmethod
|
||||||
raise ValueError("Project name or path must be provided.")
|
def find_workspace_in_path(path: Path) -> Workspace | None:
|
||||||
|
current_path = path.resolve()
|
||||||
|
|
||||||
workspace = Configuration.get("workspace")
|
for parent in [current_path] + list(current_path.parents):
|
||||||
|
workspace_file = parent / "cpl.workspace.json"
|
||||||
|
if workspace_file.exists() and workspace_file.is_file():
|
||||||
|
return Workspace.from_file(workspace_file)
|
||||||
|
|
||||||
path = Path(project)
|
return None
|
||||||
if path.exists() and path.is_dir() and (path / "cpl.project.json").exists():
|
|
||||||
return Project.from_file(path / "cpl.project.json")
|
|
||||||
|
|
||||||
if path.exists() and path.is_file():
|
@staticmethod
|
||||||
if not path.name.endswith("cpl.project.json"):
|
def create_pyproject_toml(project: Project, path: Path):
|
||||||
raise ValueError(f"File '{path}' is not a valid cpl.project.json file.")
|
|
||||||
|
|
||||||
return Project.from_file(path)
|
|
||||||
|
|
||||||
if workspace is not None:
|
|
||||||
for p in workspace.actual_projects:
|
|
||||||
if p.name == project:
|
|
||||||
return Project.from_file(Path(p.path))
|
|
||||||
|
|
||||||
if not path.is_dir() and not path.is_file():
|
|
||||||
raise ValueError(f"Unknown project {project}")
|
|
||||||
|
|
||||||
if workspace.default_project is not None:
|
|
||||||
for p in workspace.actual_projects:
|
|
||||||
if p.name == workspace.default_project:
|
|
||||||
return Project.from_file(Path(p.path))
|
|
||||||
|
|
||||||
raise ValueError(f"Project '{project}' not found.")
|
|
||||||
|
|
||||||
|
|
||||||
def create_pyproject_toml(project: Project, path: Path):
|
|
||||||
pyproject_path = path / "pyproject.toml"
|
pyproject_path = path / "pyproject.toml"
|
||||||
if pyproject_path.exists():
|
if pyproject_path.exists():
|
||||||
return
|
return
|
||||||
@@ -58,3 +46,125 @@ def create_pyproject_toml(project: Project, path: Path):
|
|||||||
).lstrip()
|
).lstrip()
|
||||||
|
|
||||||
pyproject_path.write_text(content)
|
pyproject_path.write_text(content)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_project_by_name_or_path(project: str) -> Project:
|
||||||
|
if project is None:
|
||||||
|
raise ValueError("Project name or path must be provided.")
|
||||||
|
|
||||||
|
|
||||||
|
path = Path(project)
|
||||||
|
if path.exists() and path.is_dir() and (path / "cpl.project.json").exists():
|
||||||
|
return Project.from_file(path / "cpl.project.json")
|
||||||
|
|
||||||
|
if path.exists() and path.is_file():
|
||||||
|
if not path.name.endswith("cpl.project.json"):
|
||||||
|
raise ValueError(f"File '{path}' is not a valid cpl.project.json file.")
|
||||||
|
|
||||||
|
return Project.from_file(path)
|
||||||
|
|
||||||
|
workspace = Structure.find_workspace_in_path(path.parent)
|
||||||
|
if workspace is None:
|
||||||
|
raise RuntimeError("No workspace found. Please run 'cpl init workspace' first.")
|
||||||
|
|
||||||
|
if workspace is not None:
|
||||||
|
for p in workspace.actual_projects:
|
||||||
|
if p.name == project:
|
||||||
|
return Project.from_file(Path(p.path))
|
||||||
|
|
||||||
|
if not path.is_dir() and not path.is_file():
|
||||||
|
raise ValueError(f"Unknown project {project}")
|
||||||
|
|
||||||
|
if workspace.default_project is not None:
|
||||||
|
for p in workspace.actual_projects:
|
||||||
|
if p.name == workspace.default_project:
|
||||||
|
return Project.from_file(Path(p.path))
|
||||||
|
|
||||||
|
raise ValueError(f"Project '{project}' not found.")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def init_workspace(path: Path | str, name: str):
|
||||||
|
path = Path(path) / Path("cpl.workspace.json")
|
||||||
|
|
||||||
|
if path.exists():
|
||||||
|
raise ValueError("workspace.json already exists.")
|
||||||
|
|
||||||
|
workspace = Workspace.new(str(path), name)
|
||||||
|
workspace.save()
|
||||||
|
|
||||||
|
Console.write_line(f"Created workspace '{name}'")
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def init_project(rel_path: str, name: str, project_type: str, workspace: Workspace | None, verbose=False):
|
||||||
|
if not Path(rel_path).exists():
|
||||||
|
rel_path = click.prompt(
|
||||||
|
"Project directory", type=click.Path(exists=True, file_okay=False), default="src"
|
||||||
|
)
|
||||||
|
|
||||||
|
path = Path(rel_path) / Path("cpl.project.json")
|
||||||
|
if path.exists():
|
||||||
|
Console.write_line("cpl.project.json already exists.")
|
||||||
|
return
|
||||||
|
|
||||||
|
project = Project.new(str(path), name, project_type)
|
||||||
|
|
||||||
|
executable_path = Path(project.path).parent / "main.py"
|
||||||
|
executable_file = str(executable_path.relative_to(Path(project.path).parent)) if executable_path.exists() else None
|
||||||
|
|
||||||
|
if project_type in ["console", "web", "service"]:
|
||||||
|
project.main = executable_file or click.prompt(
|
||||||
|
"Main executable", type=click.Path(exists=True, dir_okay=False), default="src/main.py"
|
||||||
|
)
|
||||||
|
|
||||||
|
project.save()
|
||||||
|
|
||||||
|
if workspace is not None:
|
||||||
|
rel_path = str(path.resolve().absolute().relative_to(Path(workspace.path).parent))
|
||||||
|
if rel_path not in workspace.projects:
|
||||||
|
workspace.projects.append(rel_path)
|
||||||
|
workspace.save()
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
Console.write_line(f"Registered '{name}' in workspace.json")
|
||||||
|
|
||||||
|
Console.write_line(f"Created {project_type} project '{name}'")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_project(path: Path, project_type: str, name: str, workspace: Workspace | None, verbose=False):
|
||||||
|
if not str(path).endswith(name):
|
||||||
|
path = path / name
|
||||||
|
|
||||||
|
if not path.exists():
|
||||||
|
os.makedirs(path, exist_ok=True)
|
||||||
|
|
||||||
|
src_dir = Path(cli.__file__).parent / ".cpl" / "new" / project_type
|
||||||
|
|
||||||
|
Console.write_line()
|
||||||
|
for root, dirs, files in os.walk(src_dir):
|
||||||
|
rel_root = Path(root).relative_to(src_dir)
|
||||||
|
target_root = path / rel_root
|
||||||
|
target_root.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for filename in files:
|
||||||
|
src_file = Path(root) / filename
|
||||||
|
tgt_file = target_root / filename
|
||||||
|
|
||||||
|
Console.set_foreground_color("green")
|
||||||
|
Console.write_line(f"Create {str(tgt_file).replace(".schematic", "")}")
|
||||||
|
Console.set_foreground_color()
|
||||||
|
|
||||||
|
if filename.endswith(".schematic"):
|
||||||
|
with open(src_file, "r") as src:
|
||||||
|
with open(str(tgt_file).replace(".schematic", ""), "w") as tgt:
|
||||||
|
tgt.write(
|
||||||
|
TemplateRenderer.render_template(
|
||||||
|
str(src_file).split(".")[0], src.read(), name, str(path)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
shutil.copy(src_file, tgt_file)
|
||||||
|
|
||||||
|
Console.write_line()
|
||||||
|
Structure.init_project(str(path), name, project_type, workspace)
|
||||||
@@ -1,2 +1 @@
|
|||||||
__version__ = "1.0.0"
|
__version__ = "1.0.0"
|
||||||
__version__ = "1.0.0"
|
|
||||||
|
|||||||
@@ -133,5 +133,4 @@ class Configuration:
|
|||||||
if isclass(key) and issubclass(key, ConfigurationModelABC) and result == default:
|
if isclass(key) and issubclass(key, ConfigurationModelABC) and result == default:
|
||||||
result = key()
|
result = key()
|
||||||
cls.set(key, result)
|
cls.set(key, result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -72,13 +72,17 @@ class Console:
|
|||||||
cls._background_color = color
|
cls._background_color = color
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_foreground_color(cls, color: Union[ForegroundColorEnum, str]):
|
def set_foreground_color(cls, color: Union[ForegroundColorEnum, str] = None):
|
||||||
r"""Sets the foreground color
|
r"""Sets the foreground color
|
||||||
|
|
||||||
Parameter:
|
Parameter:
|
||||||
color: Union[:class:`cpl.core.console.background_color_enum.BackgroundColorEnum`, :class:`str`]
|
color: Union[:class:`cpl.core.console.background_color_enum.BackgroundColorEnum`, :class:`str`]
|
||||||
Foreground color of the console
|
Foreground color of the console
|
||||||
"""
|
"""
|
||||||
|
if color is None:
|
||||||
|
cls._foreground_color = ForegroundColorEnum.default
|
||||||
|
return
|
||||||
|
|
||||||
if type(color) is str:
|
if type(color) is str:
|
||||||
cls._foreground_color = ForegroundColorEnum[color]
|
cls._foreground_color = ForegroundColorEnum[color]
|
||||||
else:
|
else:
|
||||||
|
|||||||
3
src/core/cpl/core/property.py
Normal file
3
src/core/cpl/core/property.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
class classproperty(property):
|
||||||
|
def __get__(self, obj, cls):
|
||||||
|
return self.fget(cls)
|
||||||
@@ -1 +1 @@
|
|||||||
from .graphql_app import WebApp
|
from .graphql_app import GraphQLApp
|
||||||
|
|||||||
Reference in New Issue
Block a user