diff --git a/.gitea/workflows/build-dev.yaml b/.gitea/workflows/build-dev.yaml index 3d988346..e0438905 100644 --- a/.gitea/workflows/build-dev.yaml +++ b/.gitea/workflows/build-dev.yaml @@ -33,6 +33,13 @@ jobs: working_directory: src/cpl-auth secrets: inherit + cli: + uses: ./.gitea/workflows/package.yaml + needs: [ prepare, core ] + with: + working_directory: src/cpl-cli + secrets: inherit + core: uses: ./.gitea/workflows/package.yaml needs: [prepare] diff --git a/.gitea/workflows/package.yaml b/.gitea/workflows/package.yaml index f4a6d0ce..8db3a1eb 100644 --- a/.gitea/workflows/package.yaml +++ b/.gitea/workflows/package.yaml @@ -36,6 +36,12 @@ jobs: echo "Set version to $(cat /workspace/sh-edraft.de/cpl/version.txt)" cat pyproject.toml + - name: Set package version + run: | + sed -i -E "s/^__version__ = \".*\"/__version__ = \"$(cat /workspace/sh-edraft.de/cpl/version.txt)\"/" cpl/*/__init__.py + echo "Set version to $(cat /workspace/sh-edraft.de/cpl/version.txt)" + cat cpl/*/__init__.py + - name: Set pip conf run: | cat > .pip.conf <<'EOF' diff --git a/cpl.workspace.json b/cpl.workspace.json new file mode 100644 index 00000000..c45ad1ab --- /dev/null +++ b/cpl.workspace.json @@ -0,0 +1,12 @@ +{ + "name": "cpl", + "projects": [ + "src/cli/cpl.project.json", + "src/core/cpl.project.json", + "test/cpl.project.json" + ], + "defaultProject": "cpl-cli", + "scripts": { + "format": "black src" + } +} \ No newline at end of file diff --git a/example/general/src/hosted_service.py b/example/general/src/hosted_service.py index f2fbf762..15d91753 100644 --- a/example/general/src/hosted_service.py +++ b/example/general/src/hosted_service.py @@ -3,8 +3,8 @@ from datetime import datetime from cpl.core.console import Console from cpl.core.time.cron import Cron -from cpl.dependency.hosted.cronjob import CronjobABC -from cpl.dependency.hosted.hosted_service import HostedService +from cpl.core.service.cronjob import CronjobABC +from cpl.core.service.hosted_service import HostedService class Hosted(HostedService): diff --git a/src/cpl-api/cpl/api/__init__.py b/src/api/cpl/api/__init__.py similarity index 89% rename from src/cpl-api/cpl/api/__init__.py rename to src/api/cpl/api/__init__.py index 8332d8f7..dd8eb491 100644 --- a/src/cpl-api/cpl/api/__init__.py +++ b/src/api/cpl/api/__init__.py @@ -2,3 +2,5 @@ from .error import APIError, AlreadyExists, EndpointNotImplemented, Forbidden, N from .logger import APILogger from .settings import ApiSettings from .api_module import ApiModule + +__version__ = "1.0.0" diff --git a/src/cpl-api/cpl/api/abc/__init__.py b/src/api/cpl/api/abc/__init__.py similarity index 100% rename from src/cpl-api/cpl/api/abc/__init__.py rename to src/api/cpl/api/abc/__init__.py diff --git a/src/cpl-api/cpl/api/abc/asgi_middleware_abc.py b/src/api/cpl/api/abc/asgi_middleware_abc.py similarity index 100% rename from src/cpl-api/cpl/api/abc/asgi_middleware_abc.py rename to src/api/cpl/api/abc/asgi_middleware_abc.py diff --git a/src/cpl-api/cpl/api/abc/web_app_abc.py b/src/api/cpl/api/abc/web_app_abc.py similarity index 100% rename from src/cpl-api/cpl/api/abc/web_app_abc.py rename to src/api/cpl/api/abc/web_app_abc.py diff --git a/src/cpl-api/cpl/api/api_module.py b/src/api/cpl/api/api_module.py similarity index 100% rename from src/cpl-api/cpl/api/api_module.py rename to src/api/cpl/api/api_module.py diff --git a/src/cpl-api/cpl/api/application/__init__.py b/src/api/cpl/api/application/__init__.py similarity index 100% rename from src/cpl-api/cpl/api/application/__init__.py rename to src/api/cpl/api/application/__init__.py diff --git a/src/cpl-api/cpl/api/application/web_app.py b/src/api/cpl/api/application/web_app.py similarity index 100% rename from src/cpl-api/cpl/api/application/web_app.py rename to src/api/cpl/api/application/web_app.py diff --git a/src/cpl-api/cpl/api/error.py b/src/api/cpl/api/error.py similarity index 100% rename from src/cpl-api/cpl/api/error.py rename to src/api/cpl/api/error.py diff --git a/src/cpl-api/cpl/api/logger.py b/src/api/cpl/api/logger.py similarity index 100% rename from src/cpl-api/cpl/api/logger.py rename to src/api/cpl/api/logger.py diff --git a/src/cpl-api/cpl/api/middleware/__init__.py b/src/api/cpl/api/middleware/__init__.py similarity index 100% rename from src/cpl-api/cpl/api/middleware/__init__.py rename to src/api/cpl/api/middleware/__init__.py diff --git a/src/cpl-api/cpl/api/middleware/authentication.py b/src/api/cpl/api/middleware/authentication.py similarity index 100% rename from src/cpl-api/cpl/api/middleware/authentication.py rename to src/api/cpl/api/middleware/authentication.py diff --git a/src/cpl-api/cpl/api/middleware/authorization.py b/src/api/cpl/api/middleware/authorization.py similarity index 100% rename from src/cpl-api/cpl/api/middleware/authorization.py rename to src/api/cpl/api/middleware/authorization.py diff --git a/src/cpl-api/cpl/api/middleware/logging.py b/src/api/cpl/api/middleware/logging.py similarity index 100% rename from src/cpl-api/cpl/api/middleware/logging.py rename to src/api/cpl/api/middleware/logging.py diff --git a/src/cpl-api/cpl/api/middleware/request.py b/src/api/cpl/api/middleware/request.py similarity index 100% rename from src/cpl-api/cpl/api/middleware/request.py rename to src/api/cpl/api/middleware/request.py diff --git a/src/cpl-api/cpl/api/model/__init__.py b/src/api/cpl/api/model/__init__.py similarity index 100% rename from src/cpl-api/cpl/api/model/__init__.py rename to src/api/cpl/api/model/__init__.py diff --git a/src/cpl-api/cpl/api/model/api_route.py b/src/api/cpl/api/model/api_route.py similarity index 100% rename from src/cpl-api/cpl/api/model/api_route.py rename to src/api/cpl/api/model/api_route.py diff --git a/src/cpl-api/cpl/api/model/policy.py b/src/api/cpl/api/model/policy.py similarity index 100% rename from src/cpl-api/cpl/api/model/policy.py rename to src/api/cpl/api/model/policy.py diff --git a/src/cpl-api/cpl/api/model/validation_match.py b/src/api/cpl/api/model/validation_match.py similarity index 100% rename from src/cpl-api/cpl/api/model/validation_match.py rename to src/api/cpl/api/model/validation_match.py diff --git a/src/cpl-api/cpl/api/model/websocket_route.py b/src/api/cpl/api/model/websocket_route.py similarity index 100% rename from src/cpl-api/cpl/api/model/websocket_route.py rename to src/api/cpl/api/model/websocket_route.py diff --git a/src/cpl-api/cpl/api/registry/__init__.py b/src/api/cpl/api/registry/__init__.py similarity index 100% rename from src/cpl-api/cpl/api/registry/__init__.py rename to src/api/cpl/api/registry/__init__.py diff --git a/src/cpl-api/cpl/api/registry/policy.py b/src/api/cpl/api/registry/policy.py similarity index 100% rename from src/cpl-api/cpl/api/registry/policy.py rename to src/api/cpl/api/registry/policy.py diff --git a/src/cpl-api/cpl/api/registry/route.py b/src/api/cpl/api/registry/route.py similarity index 100% rename from src/cpl-api/cpl/api/registry/route.py rename to src/api/cpl/api/registry/route.py diff --git a/src/cpl-api/cpl/api/router.py b/src/api/cpl/api/router.py similarity index 100% rename from src/cpl-api/cpl/api/router.py rename to src/api/cpl/api/router.py diff --git a/src/cpl-api/cpl/api/settings.py b/src/api/cpl/api/settings.py similarity index 100% rename from src/cpl-api/cpl/api/settings.py rename to src/api/cpl/api/settings.py diff --git a/src/cpl-api/cpl/api/typing.py b/src/api/cpl/api/typing.py similarity index 100% rename from src/cpl-api/cpl/api/typing.py rename to src/api/cpl/api/typing.py diff --git a/src/cpl-api/pyproject.toml b/src/api/pyproject.toml similarity index 100% rename from src/cpl-api/pyproject.toml rename to src/api/pyproject.toml diff --git a/src/cpl-api/requirements.dev.txt b/src/api/requirements.dev.txt similarity index 100% rename from src/cpl-api/requirements.dev.txt rename to src/api/requirements.dev.txt diff --git a/src/cpl-api/requirements.txt b/src/api/requirements.txt similarity index 100% rename from src/cpl-api/requirements.txt rename to src/api/requirements.txt diff --git a/src/cpl-application/cpl/application/__init__.py b/src/application/cpl/application/__init__.py similarity index 76% rename from src/cpl-application/cpl/application/__init__.py rename to src/application/cpl/application/__init__.py index 576b53a8..5b2d5598 100644 --- a/src/cpl-application/cpl/application/__init__.py +++ b/src/application/cpl/application/__init__.py @@ -1,2 +1,4 @@ from .application_builder import ApplicationBuilder from .host import Host + +__version__ = "1.0.0" diff --git a/src/cpl-application/cpl/application/abc/__init__.py b/src/application/cpl/application/abc/__init__.py similarity index 100% rename from src/cpl-application/cpl/application/abc/__init__.py rename to src/application/cpl/application/abc/__init__.py diff --git a/src/cpl-application/cpl/application/abc/application_abc.py b/src/application/cpl/application/abc/application_abc.py similarity index 100% rename from src/cpl-application/cpl/application/abc/application_abc.py rename to src/application/cpl/application/abc/application_abc.py diff --git a/src/cpl-application/cpl/application/abc/application_extension_abc.py b/src/application/cpl/application/abc/application_extension_abc.py similarity index 100% rename from src/cpl-application/cpl/application/abc/application_extension_abc.py rename to src/application/cpl/application/abc/application_extension_abc.py diff --git a/src/cpl-application/cpl/application/abc/startup_abc.py b/src/application/cpl/application/abc/startup_abc.py similarity index 100% rename from src/cpl-application/cpl/application/abc/startup_abc.py rename to src/application/cpl/application/abc/startup_abc.py diff --git a/src/cpl-application/cpl/application/abc/startup_extension_abc.py b/src/application/cpl/application/abc/startup_extension_abc.py similarity index 100% rename from src/cpl-application/cpl/application/abc/startup_extension_abc.py rename to src/application/cpl/application/abc/startup_extension_abc.py diff --git a/src/cpl-application/cpl/application/application_builder.py b/src/application/cpl/application/application_builder.py similarity index 98% rename from src/cpl-application/cpl/application/application_builder.py rename to src/application/cpl/application/application_builder.py index 3d3d1529..97b58154 100644 --- a/src/cpl-application/cpl/application/application_builder.py +++ b/src/application/cpl/application/application_builder.py @@ -14,7 +14,7 @@ TApp = TypeVar("TApp", bound=ApplicationABC) 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 issubclass(app, ApplicationABC), "app must be an subclass of ApplicationABC or its subclass" diff --git a/src/cpl-application/cpl/application/host.py b/src/application/cpl/application/host.py similarity index 72% rename from src/cpl-application/cpl/application/host.py rename to src/application/cpl/application/host.py index f1309beb..0aeac147 100644 --- a/src/cpl-application/cpl/application/host.py +++ b/src/application/cpl/application/host.py @@ -1,7 +1,9 @@ import asyncio 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 @@ -9,6 +11,24 @@ class Host: _loop: asyncio.AbstractEventLoop | None = None _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 def get_loop(cls) -> asyncio.AbstractEventLoop: if cls._loop is None: @@ -18,7 +38,7 @@ class Host: @classmethod def run_start_tasks(cls): - provider = get_provider() + provider = cls.get_provider() tasks = provider.get_services(StartupTask) loop = cls.get_loop() @@ -30,7 +50,7 @@ class Host: @classmethod def run_hosted_services(cls): - provider = get_provider() + provider = cls.get_provider() services = provider.get_hosted_services() loop = cls.get_loop() @@ -49,6 +69,10 @@ class Host: cls._tasks.clear() + @classmethod + async def wait_for_all(cls): + await asyncio.gather(*cls._tasks.values()) + @classmethod def run_app(cls, func: Callable, *args, **kwargs): cls.run_start_tasks() diff --git a/src/cpl-application/pyproject.toml b/src/application/pyproject.toml similarity index 100% rename from src/cpl-application/pyproject.toml rename to src/application/pyproject.toml diff --git a/src/cpl-application/requirements.dev.txt b/src/application/requirements.dev.txt similarity index 100% rename from src/cpl-application/requirements.dev.txt rename to src/application/requirements.dev.txt diff --git a/src/cpl-application/requirements.txt b/src/application/requirements.txt similarity index 100% rename from src/cpl-application/requirements.txt rename to src/application/requirements.txt diff --git a/src/cpl-auth/cpl/auth/__init__.py b/src/auth/cpl/auth/__init__.py similarity index 93% rename from src/cpl-auth/cpl/auth/__init__.py rename to src/auth/cpl/auth/__init__.py index a0f3854a..e811347f 100644 --- a/src/cpl-auth/cpl/auth/__init__.py +++ b/src/auth/cpl/auth/__init__.py @@ -4,3 +4,5 @@ from cpl.auth.keycloak.keycloak_client import KeycloakClient as _KeycloakClient from .auth_module import AuthModule from .keycloak_settings import KeycloakSettings from .logger import AuthLogger + +__version__ = "1.0.0" diff --git a/src/cpl-auth/cpl/auth/auth_module.py b/src/auth/cpl/auth/auth_module.py similarity index 100% rename from src/cpl-auth/cpl/auth/auth_module.py rename to src/auth/cpl/auth/auth_module.py diff --git a/src/cpl-auth/cpl/auth/keycloak/__init__.py b/src/auth/cpl/auth/keycloak/__init__.py similarity index 100% rename from src/cpl-auth/cpl/auth/keycloak/__init__.py rename to src/auth/cpl/auth/keycloak/__init__.py diff --git a/src/cpl-auth/cpl/auth/keycloak/keycloak_admin.py b/src/auth/cpl/auth/keycloak/keycloak_admin.py similarity index 100% rename from src/cpl-auth/cpl/auth/keycloak/keycloak_admin.py rename to src/auth/cpl/auth/keycloak/keycloak_admin.py diff --git a/src/cpl-auth/cpl/auth/keycloak/keycloak_client.py b/src/auth/cpl/auth/keycloak/keycloak_client.py similarity index 100% rename from src/cpl-auth/cpl/auth/keycloak/keycloak_client.py rename to src/auth/cpl/auth/keycloak/keycloak_client.py diff --git a/src/cpl-auth/cpl/auth/keycloak/keycloak_user.py b/src/auth/cpl/auth/keycloak/keycloak_user.py similarity index 100% rename from src/cpl-auth/cpl/auth/keycloak/keycloak_user.py rename to src/auth/cpl/auth/keycloak/keycloak_user.py diff --git a/src/cpl-auth/cpl/auth/keycloak_settings.py b/src/auth/cpl/auth/keycloak_settings.py similarity index 100% rename from src/cpl-auth/cpl/auth/keycloak_settings.py rename to src/auth/cpl/auth/keycloak_settings.py diff --git a/src/cpl-auth/cpl/auth/logger.py b/src/auth/cpl/auth/logger.py similarity index 100% rename from src/cpl-auth/cpl/auth/logger.py rename to src/auth/cpl/auth/logger.py diff --git a/src/cpl-auth/cpl/auth/permission/__init__.py b/src/auth/cpl/auth/permission/__init__.py similarity index 100% rename from src/cpl-auth/cpl/auth/permission/__init__.py rename to src/auth/cpl/auth/permission/__init__.py diff --git a/src/cpl-auth/cpl/auth/permission/permission_module.py b/src/auth/cpl/auth/permission/permission_module.py similarity index 100% rename from src/cpl-auth/cpl/auth/permission/permission_module.py rename to src/auth/cpl/auth/permission/permission_module.py diff --git a/src/cpl-auth/cpl/auth/permission/permission_seeder.py b/src/auth/cpl/auth/permission/permission_seeder.py similarity index 100% rename from src/cpl-auth/cpl/auth/permission/permission_seeder.py rename to src/auth/cpl/auth/permission/permission_seeder.py diff --git a/src/cpl-auth/cpl/auth/permission/permissions.py b/src/auth/cpl/auth/permission/permissions.py similarity index 100% rename from src/cpl-auth/cpl/auth/permission/permissions.py rename to src/auth/cpl/auth/permission/permissions.py diff --git a/src/cpl-auth/cpl/auth/permission/permissions_registry.py b/src/auth/cpl/auth/permission/permissions_registry.py similarity index 100% rename from src/cpl-auth/cpl/auth/permission/permissions_registry.py rename to src/auth/cpl/auth/permission/permissions_registry.py diff --git a/src/cpl-auth/cpl/auth/permission/role_seeder.py b/src/auth/cpl/auth/permission/role_seeder.py similarity index 100% rename from src/cpl-auth/cpl/auth/permission/role_seeder.py rename to src/auth/cpl/auth/permission/role_seeder.py diff --git a/src/cpl-auth/cpl/auth/schema/__init__.py b/src/auth/cpl/auth/schema/__init__.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/__init__.py rename to src/auth/cpl/auth/schema/__init__.py diff --git a/src/cpl-auth/cpl/auth/schema/_administration/__init__.py b/src/auth/cpl/auth/schema/_administration/__init__.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_administration/__init__.py rename to src/auth/cpl/auth/schema/_administration/__init__.py diff --git a/src/cpl-auth/cpl/auth/schema/_administration/api_key.py b/src/auth/cpl/auth/schema/_administration/api_key.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_administration/api_key.py rename to src/auth/cpl/auth/schema/_administration/api_key.py diff --git a/src/cpl-auth/cpl/auth/schema/_administration/api_key_dao.py b/src/auth/cpl/auth/schema/_administration/api_key_dao.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_administration/api_key_dao.py rename to src/auth/cpl/auth/schema/_administration/api_key_dao.py diff --git a/src/cpl-auth/cpl/auth/schema/_administration/user.py b/src/auth/cpl/auth/schema/_administration/user.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_administration/user.py rename to src/auth/cpl/auth/schema/_administration/user.py diff --git a/src/cpl-auth/cpl/auth/schema/_administration/user_dao.py b/src/auth/cpl/auth/schema/_administration/user_dao.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_administration/user_dao.py rename to src/auth/cpl/auth/schema/_administration/user_dao.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/__init__.py b/src/auth/cpl/auth/schema/_permission/__init__.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/__init__.py rename to src/auth/cpl/auth/schema/_permission/__init__.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/api_key_permission.py b/src/auth/cpl/auth/schema/_permission/api_key_permission.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/api_key_permission.py rename to src/auth/cpl/auth/schema/_permission/api_key_permission.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/api_key_permission_dao.py b/src/auth/cpl/auth/schema/_permission/api_key_permission_dao.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/api_key_permission_dao.py rename to src/auth/cpl/auth/schema/_permission/api_key_permission_dao.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/permission.py b/src/auth/cpl/auth/schema/_permission/permission.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/permission.py rename to src/auth/cpl/auth/schema/_permission/permission.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/permission_dao.py b/src/auth/cpl/auth/schema/_permission/permission_dao.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/permission_dao.py rename to src/auth/cpl/auth/schema/_permission/permission_dao.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/role.py b/src/auth/cpl/auth/schema/_permission/role.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/role.py rename to src/auth/cpl/auth/schema/_permission/role.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/role_dao.py b/src/auth/cpl/auth/schema/_permission/role_dao.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/role_dao.py rename to src/auth/cpl/auth/schema/_permission/role_dao.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/role_permission.py b/src/auth/cpl/auth/schema/_permission/role_permission.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/role_permission.py rename to src/auth/cpl/auth/schema/_permission/role_permission.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/role_permission_dao.py b/src/auth/cpl/auth/schema/_permission/role_permission_dao.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/role_permission_dao.py rename to src/auth/cpl/auth/schema/_permission/role_permission_dao.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/role_user.py b/src/auth/cpl/auth/schema/_permission/role_user.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/role_user.py rename to src/auth/cpl/auth/schema/_permission/role_user.py diff --git a/src/cpl-auth/cpl/auth/schema/_permission/role_user_dao.py b/src/auth/cpl/auth/schema/_permission/role_user_dao.py similarity index 100% rename from src/cpl-auth/cpl/auth/schema/_permission/role_user_dao.py rename to src/auth/cpl/auth/schema/_permission/role_user_dao.py diff --git a/src/cpl-auth/cpl/auth/scripts/mysql/1-users.sql b/src/auth/cpl/auth/scripts/mysql/1-users.sql similarity index 100% rename from src/cpl-auth/cpl/auth/scripts/mysql/1-users.sql rename to src/auth/cpl/auth/scripts/mysql/1-users.sql diff --git a/src/cpl-auth/cpl/auth/scripts/mysql/2-api-key.sql b/src/auth/cpl/auth/scripts/mysql/2-api-key.sql similarity index 100% rename from src/cpl-auth/cpl/auth/scripts/mysql/2-api-key.sql rename to src/auth/cpl/auth/scripts/mysql/2-api-key.sql diff --git a/src/cpl-auth/cpl/auth/scripts/mysql/3-roles-permissions.sql b/src/auth/cpl/auth/scripts/mysql/3-roles-permissions.sql similarity index 100% rename from src/cpl-auth/cpl/auth/scripts/mysql/3-roles-permissions.sql rename to src/auth/cpl/auth/scripts/mysql/3-roles-permissions.sql diff --git a/src/cpl-auth/cpl/auth/scripts/mysql/4-api-key-permissions.sql b/src/auth/cpl/auth/scripts/mysql/4-api-key-permissions.sql similarity index 100% rename from src/cpl-auth/cpl/auth/scripts/mysql/4-api-key-permissions.sql rename to src/auth/cpl/auth/scripts/mysql/4-api-key-permissions.sql diff --git a/src/cpl-auth/cpl/auth/scripts/postgres/1-users.sql b/src/auth/cpl/auth/scripts/postgres/1-users.sql similarity index 100% rename from src/cpl-auth/cpl/auth/scripts/postgres/1-users.sql rename to src/auth/cpl/auth/scripts/postgres/1-users.sql diff --git a/src/cpl-auth/cpl/auth/scripts/postgres/2-api-key.sql b/src/auth/cpl/auth/scripts/postgres/2-api-key.sql similarity index 100% rename from src/cpl-auth/cpl/auth/scripts/postgres/2-api-key.sql rename to src/auth/cpl/auth/scripts/postgres/2-api-key.sql diff --git a/src/cpl-auth/cpl/auth/scripts/postgres/3-roles-permissions.sql b/src/auth/cpl/auth/scripts/postgres/3-roles-permissions.sql similarity index 100% rename from src/cpl-auth/cpl/auth/scripts/postgres/3-roles-permissions.sql rename to src/auth/cpl/auth/scripts/postgres/3-roles-permissions.sql diff --git a/src/cpl-auth/cpl/auth/scripts/postgres/4-api-key-permissions.sql b/src/auth/cpl/auth/scripts/postgres/4-api-key-permissions.sql similarity index 100% rename from src/cpl-auth/cpl/auth/scripts/postgres/4-api-key-permissions.sql rename to src/auth/cpl/auth/scripts/postgres/4-api-key-permissions.sql diff --git a/src/cpl-auth/pyproject.toml b/src/auth/pyproject.toml similarity index 100% rename from src/cpl-auth/pyproject.toml rename to src/auth/pyproject.toml diff --git a/src/cpl-auth/requirements.dev.txt b/src/auth/requirements.dev.txt similarity index 100% rename from src/cpl-auth/requirements.dev.txt rename to src/auth/requirements.dev.txt diff --git a/src/cpl-auth/requirements.txt b/src/auth/requirements.txt similarity index 100% rename from src/cpl-auth/requirements.txt rename to src/auth/requirements.txt diff --git a/src/cli/cpl.project.json b/src/cli/cpl.project.json new file mode 100644 index 00000000..852a8a28 --- /dev/null +++ b/src/cli/cpl.project.json @@ -0,0 +1,29 @@ +{ + "name": "cpl-cli", + "version": "0.1.0", + "type": "console", + "license": "MIT", + "author": "Sven Heidemann", + "description": "CLI for the CPL library", + "homepage": "", + "keywords": [], + "dependencies": { + "click": "~8.3.0" + }, + "devDependencies": { + "black": "~25.9" + }, + "references": [], + "main": "cpl/cli/main.py", + "directory": "cpl/cli", + "build": { + "include": [ + "_templates/" + ], + "exclude": [ + "**/__pycache__", + "**/logs", + "**/tests" + ] + } +} \ No newline at end of file diff --git a/src/cli/cpl/cli/.cpl/generate/abc.py.schematic b/src/cli/cpl/cli/.cpl/generate/abc.py.schematic new file mode 100644 index 00000000..4443bc8f --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/abc.py.schematic @@ -0,0 +1,9 @@ +from abc import ABC + + +class ABC(ABC): + + def __init__(self): + ABC.__init__(self) + + print(" initialized") diff --git a/src/cli/cpl/cli/.cpl/generate/app.py.schematic b/src/cli/cpl/cli/.cpl/generate/app.py.schematic new file mode 100644 index 00000000..6d332b1e --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/app.py.schematic @@ -0,0 +1,16 @@ +from cpl.application.abc import ApplicationABC +from cpl.core.environment import Environment +from cpl.core.log import LoggerABC +from cpl.dependency import ServiceProvider +from cpl.dependency.typing import Modules + + +class (ApplicationABC): + def __init__(self, services: ServiceProvider, modules: Modules): + ApplicationABC.__init__(self, services, modules) + + self._logger = services.get_service(LoggerABC) + + async def main(self): + self._logger.debug(f"Host: {Environment.get_host_name()}") + self._logger.debug(f"Environment: {Environment.get_environment()}") diff --git a/src/cli/cpl/cli/.cpl/generate/config.py.schematic b/src/cli/cpl/cli/.cpl/generate/config.py.schematic new file mode 100644 index 00000000..a3420af4 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/config.py.schematic @@ -0,0 +1,10 @@ +from cpl.core.configuration import ConfigurationModelABC + + +class Config(ConfigurationModelABC): + + def __init__( + self, + src: dict = None, + ): + ConfigurationModelABC.__init__(self, src) diff --git a/src/cli/cpl/cli/.cpl/generate/cron_job.py.schematic b/src/cli/cpl/cli/.cpl/generate/cron_job.py.schematic new file mode 100644 index 00000000..1919b5e1 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/cron_job.py.schematic @@ -0,0 +1,9 @@ +from cpl.core.console import Console +from cpl.core.service import CronjobABC + +class CronJob(CronjobABC): + def __init__(self): + CronjobABC.__init__(self, Cron("*/1 * * * *")) + + async def loop(self): + Console.write_line(f"[{datetime.now()}] Hello, World!") diff --git a/src/cli/cpl/cli/.cpl/generate/data_access_object.py.schematic b/src/cli/cpl/cli/.cpl/generate/data_access_object.py.schematic new file mode 100644 index 00000000..4dd329d4 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/data_access_object.py.schematic @@ -0,0 +1,9 @@ +from cpl.database.abc import DbModelDaoABC + + +class Dao(DbModelDaoABC[]): + + def __init__(self): + DbModelDaoABC.__init__(self, , "") + + self.attribute(.name, str) diff --git a/src/cli/cpl/cli/.cpl/generate/db_model.py.schematic b/src/cli/cpl/cli/.cpl/generate/db_model.py.schematic new file mode 100644 index 00000000..1d4ae23b --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/db_model.py.schematic @@ -0,0 +1,23 @@ +from datetime import datetime +from typing import Self + +from cpl.core.typing import SerialId +from cpl.database.abc import DbModelABC + + +class (DbModelABC[Self]): + def __init__( + self, + id: SerialId, + name: str, + deleted: bool = False, + editor_id: SerialId | None = None, + created: datetime | None = None, + updated: datetime | None = None, + ): + DbModelABC.__init__(self, id, deleted, editor_id, created, updated) + self._name = name + + @property + def name(self) -> str: + return self._name diff --git a/src/cli/cpl/cli/.cpl/generate/db_model_join.py.schematic b/src/cli/cpl/cli/.cpl/generate/db_model_join.py.schematic new file mode 100644 index 00000000..d800a952 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/db_model_join.py.schematic @@ -0,0 +1,29 @@ +from datetime import datetime +from typing import Self + +from cpl.core.typing import SerialId +from cpl.database.abc import DbJoinModelABC + + +class Join(DbJoinModelABC[Self]): + def __init__( + self, + id: SerialId, + source_id: SerialId, + reference_id: SerialId, + deleted: bool = False, + editor_id: SerialId | None = None, + created: datetime | None = None, + updated: datetime | None = None, + ): + DbJoinModelABC.__init__(self, source_id, reference_id, id, deleted, editor_id, created, updated) + self._source_id = source_id + self._reference_id = reference_id + + @property + def source_id(self) -> int: + return self._source_id + + @property + def reference(self) -> int: + return self._reference_id diff --git a/src/cli/cpl/cli/.cpl/generate/enum.py.schematic b/src/cli/cpl/cli/.cpl/generate/enum.py.schematic new file mode 100644 index 00000000..ed2e4e94 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/enum.py.schematic @@ -0,0 +1,5 @@ +from enum import Enum + + +class Enum(Enum): + KEY = "value" diff --git a/src/cli/cpl/cli/.cpl/generate/hosted_service.py.schematic b/src/cli/cpl/cli/.cpl/generate/hosted_service.py.schematic new file mode 100644 index 00000000..4bd8f6f5 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/hosted_service.py.schematic @@ -0,0 +1,13 @@ +from cpl.core.console import Console +from cpl.core.service import HostedService + + +class (HostedService): + def __init__(self): + HostedService.__init__(self) + + async def start(self): + Console.write_line("Hello, World!") + + async def stop(self): + Console.write_line("Goodbye, World!") diff --git a/src/cli/cpl/cli/.cpl/generate/logger.py.schematic b/src/cli/cpl/cli/.cpl/generate/logger.py.schematic new file mode 100644 index 00000000..aa9b144f --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/logger.py.schematic @@ -0,0 +1,7 @@ +from cpl.core.log.wrapped_logger import WrappedLogger + + +class Logger(WrappedLogger): + + def __init__(self): + WrappedLogger.__init__(self, "") diff --git a/src/cli/cpl/cli/.cpl/generate/module.py.schematic b/src/cli/cpl/cli/.cpl/generate/module.py.schematic new file mode 100644 index 00000000..a4901060 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/module.py.schematic @@ -0,0 +1,17 @@ +from cpl.dependency import ServiceCollection, ServiceProvider +from cpl.dependency.module import Module + + +class Module(Module): + dependencies = [] + configuration = [] + singleton = [] + scoped = [] + transient = [] + hosted = [] + + @staticmethod + def register(collection: ServiceCollection): ... + + @staticmethod + def configure(provider: ServiceProvider): ... diff --git a/src/cli/cpl/cli/.cpl/generate/multiprocess.py.schematic b/src/cli/cpl/cli/.cpl/generate/multiprocess.py.schematic new file mode 100644 index 00000000..be7fa608 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/multiprocess.py.schematic @@ -0,0 +1,9 @@ +import multiprocessing + + +class (multiprocessing.Process): + + def __init__(self): + multiprocessing.Process.__init__(self) + + def run(self): ... \ No newline at end of file diff --git a/src/cli/cpl/cli/.cpl/generate/pipe.py.schematic b/src/cli/cpl/cli/.cpl/generate/pipe.py.schematic new file mode 100644 index 00000000..65a30bd0 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/pipe.py.schematic @@ -0,0 +1,11 @@ +from cpl.core.pipes import PipeABC +from cpl.core.typing import T + + +class Pipe(PipeABC): + + @staticmethod + def to_str(value: T, *args) -> str: ... + + @staticmethod + def from_str(value: str, *args) -> T: ... diff --git a/src/cli/cpl/cli/.cpl/generate/thread.py.schematic b/src/cli/cpl/cli/.cpl/generate/thread.py.schematic new file mode 100644 index 00000000..438681e6 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/thread.py.schematic @@ -0,0 +1,9 @@ +import threading + + +class (threading.Thread): + + def __init__(self): + threading.Thread.__init__(self) + + def run(self): ... \ No newline at end of file diff --git a/src/cli/cpl/cli/.cpl/generate/web_app.py.schematic b/src/cli/cpl/cli/.cpl/generate/web_app.py.schematic new file mode 100644 index 00000000..5f63cc0b --- /dev/null +++ b/src/cli/cpl/cli/.cpl/generate/web_app.py.schematic @@ -0,0 +1,18 @@ +from cpl.api.application import WebApp +from cpl.core.environment import Environment +from cpl.core.log import LoggerABC +from cpl.dependency import ServiceProvider +from cpl.dependency.typing import Modules + + +class (WebApp): + def __init__(self, services: ServiceProvider, modules: Modules): + WebApp.__init__(self, services, modules) + + self._logger = services.get_service(LoggerABC) + + async def main(self): + self._logger.debug(f"Host: {Environment.get_host_name()}") + self._logger.debug(f"Environment: {Environment.get_environment()}") + + await super().main() diff --git a/src/cli/cpl/cli/.cpl/new/console/main.py b/src/cli/cpl/cli/.cpl/new/console/main.py new file mode 100644 index 00000000..0b4aff83 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/new/console/main.py @@ -0,0 +1,9 @@ +from cpl.core.console import Console + + +def main(): + Console.write_line("Hello, World!") + + +if __name__ == "__main__": + main() diff --git a/src/cli/cpl/cli/.cpl/new/graphql/main.py b/src/cli/cpl/cli/.cpl/new/graphql/main.py new file mode 100644 index 00000000..4c1e3a6e --- /dev/null +++ b/src/cli/cpl/cli/.cpl/new/graphql/main.py @@ -0,0 +1,46 @@ +from cpl.api import ApiModule +from cpl.application import ApplicationBuilder +from cpl.core.configuration import Configuration +from cpl.graphql.application import GraphQLApp +from starlette.responses import JSONResponse + + +def main(): + builder = ApplicationBuilder[GraphQLApp](GraphQLApp) + + Configuration.add_json_file(f"appsettings.json", optional=True) + + ( + builder.services.add_logging() + # uncomment to add preferred database module + # .add_module(MySQLModule) + # .add_module(PostgresModule) + .add_module(ApiModule) + ) + + app = builder.build() + app.with_logging() + + app.with_authentication() + app.with_authorization() + + app.with_route( + path="/ping", + fn=lambda r: JSONResponse("pong"), + method="GET", + ) + + schema = app.with_graphql() + schema.query.string_field("ping", resolver=lambda: "pong") + + app.with_auth_root_queries(True) + app.with_auth_root_mutations(True) + + app.with_playground() + app.with_graphiql() + + app.run() + + +if __name__ == "__main__": + main() diff --git a/src/cli/cpl/cli/.cpl/new/library/class.py b/src/cli/cpl/cli/.cpl/new/library/class.py new file mode 100644 index 00000000..84d43c42 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/new/library/class.py @@ -0,0 +1,3 @@ +class Class1: + + def __init__(self): ... diff --git a/src/cli/cpl/cli/.cpl/new/service/main.py b/src/cli/cpl/cli/.cpl/new/service/main.py new file mode 100644 index 00000000..41829500 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/new/service/main.py @@ -0,0 +1,13 @@ +from cpl.application import Host +from my_hosted_service import MyHostedService + + +async def main(): + Host.services.add_hosted_service(MyHostedService) + Host.run_start_tasks() + Host.run_hosted_services() + await Host.wait_for_all() + + +if __name__ == "__main__": + Host.run(main) diff --git a/src/cli/cpl/cli/.cpl/new/service/my_hosted_service.py b/src/cli/cpl/cli/.cpl/new/service/my_hosted_service.py new file mode 100644 index 00000000..016b8c61 --- /dev/null +++ b/src/cli/cpl/cli/.cpl/new/service/my_hosted_service.py @@ -0,0 +1,13 @@ +from cpl.core.console import Console +from cpl.dependency.hosted import HostedService + + +class MyHostedService(HostedService): + def __init__(self): + HostedService.__init__(self) + + async def start(self): + Console.write_line("Hello, World!") + + async def stop(self): + Console.write_line("Goodbye, World!") diff --git a/src/cli/cpl/cli/.cpl/new/web/main.py b/src/cli/cpl/cli/.cpl/new/web/main.py new file mode 100644 index 00000000..4d23fa9f --- /dev/null +++ b/src/cli/cpl/cli/.cpl/new/web/main.py @@ -0,0 +1,37 @@ +from starlette.responses import JSONResponse + +from cpl.api import ApiModule +from cpl.api.application import WebApp +from cpl.application import ApplicationBuilder +from cpl.core.configuration import Configuration + + +def main(): + builder = ApplicationBuilder[WebApp](WebApp) + + Configuration.add_json_file(f"appsettings.json", optional=True) + + ( + builder.services.add_logging() + # uncomment to add preferred database module + # .add_module(MySQLModule) + # .add_module(PostgresModule) + .add_module(ApiModule) + ) + + app = builder.build() + app.with_logging() + + app.with_authentication() + app.with_authorization() + + app.with_route( + path="/ping", + fn=lambda r: JSONResponse("pong"), + method="GET", + ) + app.run() + + +if __name__ == "__main__": + main() diff --git a/src/cli/cpl/cli/__init__.py b/src/cli/cpl/cli/__init__.py new file mode 100644 index 00000000..5becc17c --- /dev/null +++ b/src/cli/cpl/cli/__init__.py @@ -0,0 +1 @@ +__version__ = "1.0.0" diff --git a/src/cli/cpl/cli/cli.py b/src/cli/cpl/cli/cli.py new file mode 100644 index 00000000..47ff710b --- /dev/null +++ b/src/cli/cpl/cli/cli.py @@ -0,0 +1,55 @@ +import traceback + +import click + +from cpl.core.console import Console + + +class AliasedGroup(click.Group): + def command(self, *args, **kwargs): + aliases = kwargs.pop("aliases", []) + + def decorator(f): + cmd = super(AliasedGroup, self).command(*args, **kwargs)(f) + cmd.callback = self._handle_errors(cmd.callback) + + for alias in aliases: + self.add_command(cmd, alias) + return cmd + + return decorator + + def format_commands(self, ctx, formatter): + commands = [] + seen = set() + for name, cmd in self.commands.items(): + if cmd in seen: + continue + seen.add(cmd) + aliases = [a for a, c in self.commands.items() if c is cmd and a != name] + alias_text = f" (aliases: {', '.join(aliases)})" if aliases else "" + commands.append((name, f"{cmd.short_help or ''}{alias_text}")) + + with formatter.section("Commands"): + formatter.write_dl(commands) + + @staticmethod + def _handle_errors(f): + def wrapper(*args, **kwargs): + try: + res = f(*args, **kwargs) + Console.write_line() + return res + except Exception as e: + tb = None + if "verbose" in kwargs and kwargs["verbose"]: + tb = traceback.format_exc() + Console.error(str(e), tb) + Console.write_line() + exit(-1) + + return wrapper + + +@click.group(cls=AliasedGroup) +def cli(): ... diff --git a/src/cpl-core/cpl/core/__init__.py b/src/cli/cpl/cli/command/__init__.py similarity index 100% rename from src/cpl-core/cpl/core/__init__.py rename to src/cli/cpl/cli/command/__init__.py diff --git a/src/cpl-core/cpl/core/abc/__init__.py b/src/cli/cpl/cli/command/package/__init__.py similarity index 100% rename from src/cpl-core/cpl/core/abc/__init__.py rename to src/cli/cpl/cli/command/package/__init__.py diff --git a/src/cli/cpl/cli/command/package/add.py b/src/cli/cpl/cli/command/package/add.py new file mode 100644 index 00000000..c5c21e63 --- /dev/null +++ b/src/cli/cpl/cli/command/package/add.py @@ -0,0 +1,27 @@ +from pathlib import Path + +import click + +from cpl.cli.cli import cli +from cpl.cli.utils.structure import Structure +from cpl.core.console import Console + + +@cli.command("add", aliases=["a"]) +@click.argument("reference", type=click.STRING, required=True) +@click.argument("target", type=click.STRING, required=True) +@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output") +def add(reference: str, target: str, verbose: bool): + reference_project = Structure.get_project_by_name_or_path(reference) + target_project = Structure.get_project_by_name_or_path(target) + + if reference_project.name == target_project.name: + raise ValueError("Cannot add a project as a dependency to itself!") + + if reference_project.path in target_project.references: + raise ValueError(f"Project '{reference_project.name}' is already a reference of '{target_project.name}'") + + rel_path = Path(reference_project.path).relative_to(Path(target_project.path).parent, walk_up=True) + target_project.references.append(str(rel_path)) + target_project.save() + Console.write_line(f"Added '{reference_project.name}' to '{target_project.name}' project") diff --git a/src/cli/cpl/cli/command/package/install.py b/src/cli/cpl/cli/command/package/install.py new file mode 100644 index 00000000..c1c2a195 --- /dev/null +++ b/src/cli/cpl/cli/command/package/install.py @@ -0,0 +1,69 @@ +import os +import subprocess +from pathlib import Path + +import click + +from cpl.cli.cli import cli +from cpl.cli.const import PIP_URL +from cpl.cli.utils.pip import Pip +from cpl.cli.utils.structure import Structure +from cpl.core.console import Console + + +@cli.command("install", aliases=["i"]) +@click.argument("package", type=click.STRING, required=False) +@click.argument("project", type=click.STRING, required=False) +@click.option("--dev", is_flag=True, help="Include dev dependencies") +@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output") +def install(package: str, project: str, dev: bool, verbose: bool): + project = Structure.get_project_by_name_or_path(project or "./") + + if package is not None: + Console.write_line(f"Installing {package} to '{project.name}':") + try: + Pip.command( + f"install --extra-index-url {PIP_URL}", + package, + verbose=verbose, + path=Path(project.path).parent, + ) + except subprocess.CalledProcessError as e: + Console.error(f"Failed to install {package}: exit code {e.returncode}") + return + + package_name = Pip.get_package_without_version(package) + installed_version = Pip.get_package_version(package_name, path=project.path) + if installed_version is None: + Console.error(f"Package '{package_name}' not found after installation.") + return + + deps = project.dependencies if not dev else project.dev_dependencies + deps[package_name] = Pip.apply_prefix(installed_version, Pip.get_package_full_version(package)) + + project.save() + Console.write_line(f"Added {package_name}~{installed_version} to project dependencies.") + return + + deps: dict = project.dependencies + if dev: + deps.update(project.dev_dependencies) + + if not deps: + Console.error("No dependencies to install.") + return + + Console.write_line(f"Installing dependencies for '{project.name}':") + + for name, version in deps.items(): + dep = Pip.normalize_dep(name, version) + Console.write_line(f" -> {dep}") + try: + Pip.command( + "install --extra-index-url https://git.sh-edraft.de/api/packages/sh-edraft.de/pypi/simple/", + dep, + verbose=verbose, + path=project.path, + ) + except subprocess.CalledProcessError as e: + Console.error(f"Failed to install {dep}: exit code {e.returncode}") diff --git a/src/cli/cpl/cli/command/package/remove.py b/src/cli/cpl/cli/command/package/remove.py new file mode 100644 index 00000000..c1e8e3d6 --- /dev/null +++ b/src/cli/cpl/cli/command/package/remove.py @@ -0,0 +1,26 @@ +from pathlib import Path + +import click + +from cpl.cli.cli import cli +from cpl.cli.utils.structure import Structure +from cpl.core.console import Console + + +@cli.command("remove", aliases=["rm"]) +@click.argument("reference", type=click.STRING, required=True) +@click.argument("target", type=click.STRING, required=True) +def remove(reference: str, target: str): + reference_project = Structure.get_project_by_name_or_path(reference) + target_project = Structure.get_project_by_name_or_path(target) + + if reference_project.name == target_project.name: + raise ValueError("Cannot add a project as a dependency to itself!") + + rel_path = str(Path(reference_project.path).relative_to(Path(target_project.path).parent, walk_up=True)) + if rel_path not in target_project.references: + raise ValueError(f"Project '{reference_project.name}' isn't a reference of '{target_project.name}'") + + target_project.references.remove(rel_path) + target_project.save() + Console.write_line(f"Removed '{reference_project.name}' from '{target_project.name}' project") diff --git a/src/cli/cpl/cli/command/package/uninstall.py b/src/cli/cpl/cli/command/package/uninstall.py new file mode 100644 index 00000000..9718c608 --- /dev/null +++ b/src/cli/cpl/cli/command/package/uninstall.py @@ -0,0 +1,41 @@ +import subprocess + +import click + +from cpl.cli.cli import cli +from cpl.cli.utils.pip import Pip +from cpl.cli.utils.structure import Structure +from cpl.core.console import Console + + +@cli.command("uninstall", aliases=["ui"]) +@click.argument("package", required=False) +@click.argument("project", type=click.STRING, required=False) +@click.option("--dev", is_flag=True, help="Include dev dependencies") +@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output") +def uninstall(package: str, project: str, dev: bool, verbose: bool): + if package is None: + package = Console.read("Package name to uninstall: ").strip() + + project = Structure.get_project_by_name_or_path(project or "./") + + deps = project.dependencies if not dev else project.dev_dependencies + + try: + Pip.command( + "uninstall -y", + package, + verbose=verbose, + path=project.path, + ) + except subprocess.CalledProcessError as e: + Console.error(f"Failed to uninstall {package}: exit code {e.returncode}") + return + + if package in deps: + del deps[package] + project.save() + Console.write_line(f"Removed {package} from project dependencies.") + return + + Console.write_line(f"Package {package} was not found in project dependencies.") diff --git a/src/cli/cpl/cli/command/package/update.py b/src/cli/cpl/cli/command/package/update.py new file mode 100644 index 00000000..6268bdba --- /dev/null +++ b/src/cli/cpl/cli/command/package/update.py @@ -0,0 +1,74 @@ +import subprocess + +import click + +from cpl.cli.cli import cli +from cpl.cli.const import PIP_URL +from cpl.cli.utils.pip import Pip +from cpl.cli.utils.structure import Structure +from cpl.core.console import Console + + +@cli.command("update", aliases=["u"]) +@click.argument("package", type=click.STRING, required=False) +@click.argument("project", type=click.STRING, required=False) +@click.option("--dev", is_flag=True, help="Include dev dependencies") +@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output") +def update(package: str, project: str, dev: bool, verbose: bool): + project = Structure.get_project_by_name_or_path(project or "./") + + deps: dict = project.dependencies + if dev: + deps = project.dev_dependencies + + if package is not None: + if package not in deps: + Console.error(f"Package '{package}' not installed.") + return + + old_spec = deps[package] + + Console.write_line(f"Updating {package} to '{project.name}':") + try: + Pip.command( + f"install --upgrade --extra-index-url {PIP_URL}" f"{Pip.normalize_dep(package, old_spec)}", + verbose=verbose, + path=project.path, + ) + except subprocess.CalledProcessError as e: + Console.error(f"Failed to install {package}: exit code {e.returncode}") + return + + installed_version = Pip.get_package_version(package, path=project.path) + if installed_version is None: + Console.error(f"Package '{package}' not found after update.") + return + + deps[package] = Pip.apply_prefix(installed_version, old_spec) + project.save() + Console.write_line(f"Updated {package} to {deps[package]}") + return + + if not deps: + Console.error("No dependencies to install.") + return + + Console.write_line(f"Updating dependencies for '{project.name}':") + + for name, version in list(deps.items()): + dep = Pip.normalize_dep(name, version) + Console.write_line(f" -> {dep}") + try: + Pip.command("install --upgrade", dep, verbose=verbose, path=project.path) + except subprocess.CalledProcessError as e: + Console.error(f"Failed to update {dep}: exit code {e.returncode}") + return + + installed_version = Pip.get_package_version(name, path=project.path) + if installed_version is None: + Console.error(f"Package '{name}' not found after update.") + continue + + deps[name] = Pip.apply_prefix(installed_version, version) + + project.save() diff --git a/src/cpl-dependency/cpl/dependency/module/__init__.py b/src/cli/cpl/cli/command/project/__init__.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/module/__init__.py rename to src/cli/cpl/cli/command/project/__init__.py diff --git a/src/cli/cpl/cli/command/project/build.py b/src/cli/cpl/cli/command/project/build.py new file mode 100644 index 00000000..931e7266 --- /dev/null +++ b/src/cli/cpl/cli/command/project/build.py @@ -0,0 +1,73 @@ +import os.path +import subprocess +from pathlib import Path + +import click + +from cpl.cli.cli import cli +from cpl.cli.utils.venv import ensure_venv, get_venv_python +from cpl.core.configuration import Configuration +from cpl.core.console import Console + + +@cli.command("build", aliases=["b"]) +@click.argument("project", type=click.STRING, required=False) +@click.option("--dist", "-d", type=str) +@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") +def build(project: str, dist: str = None, skip_py_build: bool = None, verbose: bool = None): + from cpl.cli.utils.structure import Structure + + project = Structure.get_project_by_name_or_path(project or "./") + venv = ensure_venv().absolute() + dist_path = dist or Path(project.path).parent / "dist" + + if dist is None and Configuration.get("workspace") is not None: + dist_path = Path(Configuration.get("workspace").path).parent / "dist" + + dist_path = Path(dist_path).resolve().absolute() + + if verbose: + Console.write_line(f"Creating dist folder at {dist_path}...") + + os.makedirs(dist_path, exist_ok=True) + + project.do_build(dist_path, verbose) + + if skip_py_build: + Console.write_line("\nDone!") + return + + Structure.create_pyproject_toml(project, dist_path / project.name) + python = str(get_venv_python(venv)) + + result = Console.spinner( + "Building python package...", + lambda: subprocess.run( + [ + python, + "-m", + "build", + "--outdir", + str(dist_path / project.name), + str(dist_path / project.name), + ], + check=True, + stdin=subprocess.DEVNULL if not verbose else None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ), + ) + + if result is None: + raise RuntimeError("Build process did not run") + + if verbose and result.stdout is not None: + Console.write_line(result.stdout.decode()) + + if result.returncode != 0 and result.stderr is not None: + if result.stderr is not None: + Console.error(str(result.stderr.decode())) + raise RuntimeError(f"Build process failed with exit code {result.returncode}") + + Console.write_line(" Done!") diff --git a/src/cli/cpl/cli/command/project/run.py b/src/cli/cpl/cli/command/project/run.py new file mode 100644 index 00000000..232cc340 --- /dev/null +++ b/src/cli/cpl/cli/command/project/run.py @@ -0,0 +1,55 @@ +import os +import subprocess +from pathlib import Path + +import click + +from cpl.cli.cli import cli +from cpl.cli.utils.structure import Structure +from cpl.cli.utils.venv import get_venv_python, ensure_venv +from cpl.core.configuration import Configuration +from cpl.core.console import Console + + +@cli.command("run", aliases=["r"]) +@click.argument("project", type=str, required=False, default=None) +@click.argument("args", nargs=-1) +@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") +def run(project: str, args: list[str], dev: bool, verbose: bool): + project_path = Path("./") + if project is not None: + project_path = (Path("./") / project).resolve().absolute() + + project = Structure.get_project_by_name_or_path(str(project_path)) + if project.main is None: + Console.error(f"Project {project.name} has no executable") + return + + path = str(Path(project.path).parent.resolve().absolute()) + executable = project.main + if not dev: + dist_path = Path(project.path).parent / "dist" + + if Configuration.get("workspace") is not None: + dist_path = Path(Configuration.get("workspace").path).parent / "dist" + + dist_path = Path(dist_path).resolve().absolute() + if verbose: + Console.write_line(f"Creating dist folder at {dist_path}...") + + os.makedirs(dist_path, exist_ok=True) + project.do_build(dist_path, verbose) + path = dist_path / project.name + main = project.main.replace(project.directory, "").lstrip("/\\") + + executable = path / main + + python = str(get_venv_python(ensure_venv()).absolute()) + Console.write_line(f"\nStarting project {project.name}...") + if verbose: + Console.write_line(f" with args {args}...") + + Console.write_line("\n\n") + + subprocess.run([python, executable, *args], cwd=path) diff --git a/src/cli/cpl/cli/command/project/start.py b/src/cli/cpl/cli/command/project/start.py new file mode 100644 index 00000000..28bc4797 --- /dev/null +++ b/src/cli/cpl/cli/command/project/start.py @@ -0,0 +1,31 @@ +from pathlib import Path + +import click + +from cpl.cli.cli import cli +from cpl.cli.utils.live_server.live_server import LiveServer +from cpl.cli.utils.structure import Structure +from cpl.core.console import Console + + +@cli.command("start", aliases=["s"]) +@click.argument("project", type=str, required=False, default=None) +@click.argument("args", nargs=-1) +@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") +def start(project: str, args: list[str], dev: bool, verbose: bool): + project_path = Path("./") + if project is not None: + project_path = (Path("./") / project).resolve().absolute() + + project = Structure.get_project_by_name_or_path(str(project_path)) + if project.main is None: + Console.error(f"Project {project.name} has no executable") + return + + LiveServer( + project, + args, + dev, + verbose, + ).start() diff --git a/src/cpl-graphql/cpl/graphql/__init__.py b/src/cli/cpl/cli/command/structure/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/__init__.py rename to src/cli/cpl/cli/command/structure/__init__.py diff --git a/src/cli/cpl/cli/command/structure/generate.py b/src/cli/cpl/cli/command/structure/generate.py new file mode 100644 index 00000000..67e74096 --- /dev/null +++ b/src/cli/cpl/cli/command/structure/generate.py @@ -0,0 +1,104 @@ +import os +from pathlib import Path + +import click + +from cpl.cli import cli as clim +from cpl.cli.cli import cli +from cpl.cli.model.project import Project +from cpl.cli.model.workspace import Workspace +from cpl.cli.utils.structure import Structure +from cpl.cli.utils.template_collector import TemplateCollector +from cpl.cli.utils.template_renderer import TemplateRenderer +from cpl.core.configuration import Configuration +from cpl.core.console import Console + + +@cli.command("generate", aliases=["g"]) +@click.argument("schematic", type=click.STRING, required=True) +@click.argument("name", type=click.STRING, required=True) +@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output") +def generate(schematic: str, name: str, verbose: bool) -> None: + TemplateCollector.collect_templates(Path(clim.__file__).parent, verbose) + + workspace: Workspace = Configuration.get("workspace") + if workspace is not None: + if verbose: + Console.write_line("Workspace found, collecting templates...") + TemplateCollector.collect_templates(Path(workspace.path).parent, verbose) + + project = None + try: + project = Structure.get_project_by_name_or_path("./") + if verbose: + Console.write_line("project found, collecting templates...") + + TemplateCollector.collect_templates(Path(project.path).parent, verbose) + except ValueError: + if verbose: + Console.write_line("Local project not found") + + templates = TemplateCollector.get_templates() + schematics = {} + for template_name, template_content in templates.items(): + t_name = template_name.split(".")[0] + + if t_name in schematics: + raise ValueError(f"Duplicate schematic name found: {t_name}") + + schematics[t_name] = template_name + + for i in range(len(t_name)): + char = t_name[i] + if char in schematics: + continue + + schematics[char] = template_name + break + + if schematic not in schematics: + raise ValueError( + f"Schematic '{schematic}' not found. Available schematics: {', '.join([x.split(".")[0] for x in templates.keys()])}" + ) + + path, name = _get_name_and_path_from_name(name, project) + + os.makedirs(path, exist_ok=True) + + Console.write_line(f"Generating {str(path / name)} ...") + with open(path / f"{name}.py", "w") as f: + f.write( + TemplateRenderer.render_template( + schematics[schematic].split(".")[0], templates[schematics[schematic]], name, str(path) + ) + ) + + +def _get_name_and_path_from_name(in_name: str, project: Project = None) -> tuple[Path, str]: + path = "" + name = "" + + in_name_parts = in_name.split("/") + if len(in_name_parts) == 1: + name = in_name_parts[0] + else: + path = "/".join(in_name_parts[:-1]) + name = in_name_parts[-1] + + workspace: Workspace = Configuration.get("workspace") + if workspace is None and project is not None: + return (Path(project.path).parent / project.directory / path).resolve().absolute(), name + elif workspace is None and project is None: + return Path(path).resolve().absolute(), name + + selected_project = project + project_name = path.split("/")[0] + project_by_name = workspace.get_project_by_name(project_name) + if project_by_name is not None: + selected_project = project_by_name + path = "/".join(path.split("/")[1:]) + + if selected_project is None: + selected_project = workspace.get_project_by_name(workspace.default_project) + + return (Path(selected_project.path).parent / selected_project.directory / path).resolve().absolute(), name diff --git a/src/cli/cpl/cli/command/structure/init.py b/src/cli/cpl/cli/command/structure/init.py new file mode 100644 index 00000000..ee7c4d7f --- /dev/null +++ b/src/cli/cpl/cli/command/structure/init.py @@ -0,0 +1,41 @@ +from pathlib import Path + +import click + +from cpl.cli.const import PROJECT_TYPES, PROJECT_TYPES_SHORT +from cpl.cli.utils.prompt import ProjectType +from cpl.cli.utils.structure import Structure +from cpl.cli.utils.venv import ensure_venv +from cpl.core.console import Console + + +@click.command("init") +@click.argument("target", required=False) +@click.argument("name", required=False) +def init(target: str, name: str): + workspace = None + project = None + + if target is None: + Console.write_line("CPL Init Wizard") + target = click.prompt( + "What do you want to initialize?", + type=ProjectType, + show_choices=True, + ) + + if target in PROJECT_TYPES_SHORT: + target = [pt for pt in PROJECT_TYPES if pt.startswith(target)][0] + + if target in ["workspace", "ws"]: + workspace = Structure.init_workspace("./", name or click.prompt("Workspace name", default="my-workspace")) + elif target in PROJECT_TYPES: + workspace = Structure.find_workspace_in_path(Path(name).parent) + project = Structure.init_project( + "./", name or click.prompt("Project name", default=f"my-{target}"), target, workspace + ) + else: + Console.error(f"Unknown target '{target}'") + raise SystemExit(1) + + ensure_venv(Path((workspace or project).path).parent) diff --git a/src/cli/cpl/cli/command/structure/new.py b/src/cli/cpl/cli/command/structure/new.py new file mode 100644 index 00000000..7476d8c0 --- /dev/null +++ b/src/cli/cpl/cli/command/structure/new.py @@ -0,0 +1,70 @@ +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.prompt import ProjectType +from cpl.cli.utils.structure import Structure +from cpl.cli.utils.venv import ensure_venv +from cpl.core.console import Console + + +@cli.command("new", aliases=["n"]) +@click.argument("type", type=ProjectType, 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=" ", + 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: + 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") + ensure_venv(Path(name)) + + if project is None or len(project) != 2: + return + + 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, with_parents=False) + 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) + + ensure_venv(Path((workspace or project).path).parent) + if workspace.default_project is None: + workspace.default_project = project_name + workspace.save() diff --git a/src/cli/cpl/cli/command/version.py b/src/cli/cpl/cli/command/version.py new file mode 100644 index 00000000..5709f43a --- /dev/null +++ b/src/cli/cpl/cli/command/version.py @@ -0,0 +1,27 @@ +import platform + +import cpl +from cpl.cli.cli import cli +from cpl.cli.utils.pip import Pip +from cpl.core.console import Console, ForegroundColorEnum + + +@cli.command("version", aliases=["v"]) +def version(): + Console.set_foreground_color(ForegroundColorEnum.yellow) + Console.banner("CPL CLI") + Console.set_foreground_color(ForegroundColorEnum.default) + + Console.write_line() + Console.write_line(f"CPL CLI: {getattr(cpl.cli, '__version__', "1.0")}") + Console.write_line(f"Python: {platform.python_version()}") + Console.write_line(f"PIP: {Pip.get_pip_version()}") + Console.write_line(f"OS: {platform.system()} {platform.release()}") + + Console.write_line("\nCPL Packages:\n") + cpl_packages = {n: v for n, v in Pip.get_packages().items() if n.startswith("cpl-")} + if len(cpl_packages) == 0: + Console.write_line("No CPL packages installed") + return + + Console.table(["Package", "Version"], [[n, v] for n, v in cpl_packages.items()]) diff --git a/src/cli/cpl/cli/const.py b/src/cli/cpl/cli/const.py new file mode 100644 index 00000000..acb63ff7 --- /dev/null +++ b/src/cli/cpl/cli/const.py @@ -0,0 +1,4 @@ +PROJECT_TYPES = ["console", "web", "graphql", "library", "service"] +PROJECT_TYPES_SHORT = [x[0] for x in PROJECT_TYPES] + +PIP_URL = "https://git.sh-edraft.de/api/packages/sh-edraft.de/pypi/simple/" diff --git a/src/cli/cpl/cli/main.py b/src/cli/cpl/cli/main.py new file mode 100644 index 00000000..baae0e58 --- /dev/null +++ b/src/cli/cpl/cli/main.py @@ -0,0 +1,95 @@ +from pathlib import Path + +from cpl.cli.cli import cli +from cpl.cli.command.package.add import add +from cpl.cli.command.package.install import install +from cpl.cli.command.package.remove import remove +from cpl.cli.command.package.uninstall import uninstall +from cpl.cli.command.package.update import update +from cpl.cli.command.project.build import build +from cpl.cli.command.project.run import run +from cpl.cli.command.project.start import start +from cpl.cli.command.structure.generate import generate +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.model.workspace import Workspace +from cpl.cli.utils.custom_command import script_command +from cpl.core.configuration import Configuration +from cpl.core.console import Console + + +def _load_workspace(path: str) -> Workspace | None: + path = Path(path) + if not path.exists() or path.is_dir(): + return None + + return Workspace.from_file(path) + + +def _load_scripts(): + for p in [ + "./cpl.workspace.json", + "../cpl.workspace.json", + "../../cpl.workspace.json", + ]: + ws = _load_workspace(p) + if ws is None: + continue + + Configuration.set("workspace", ws) + return ws.scripts + + return {} + + +def prepare(): + scripts = _load_scripts() + for name, command in scripts.items(): + script_command(cli, name, command) + + +def configure(): + # cli + cli.add_command(version) + + # structure + cli.add_command(init) + cli.add_command(new) + cli.add_command(generate) + + # packaging + cli.add_command(install) + cli.add_command(uninstall) + cli.add_command(update) + cli.add_command(add) + cli.add_command(remove) + + # run + cli.add_command(build) + cli.add_command(run) + cli.add_command(start) + + +def main(): + prepare() + configure() + try: + cli() + finally: + Console.write_line() + + +if __name__ == "__main__": + main() + + +# (( +# ( `) +# ; / , +# / \/ +# / | +# / ~/ +# / ) ) ~ edraft +# ___// | / +# `--' \_~-, diff --git a/src/cpl-graphql/cpl/graphql/_endpoints/__init__.py b/src/cli/cpl/cli/model/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/_endpoints/__init__.py rename to src/cli/cpl/cli/model/__init__.py diff --git a/src/cli/cpl/cli/model/build.py b/src/cli/cpl/cli/model/build.py new file mode 100644 index 00000000..b9939e29 --- /dev/null +++ b/src/cli/cpl/cli/model/build.py @@ -0,0 +1,22 @@ +from cpl.cli.model.cpl_sub_structure_model import 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]): + CPLSubStructureModel.__init__(self) + + self._include = include + self._exclude = exclude + + @property + def include(self) -> list[str]: + return self._include + + @property + def exclude(self) -> list[str]: + return self._exclude diff --git a/src/cli/cpl/cli/model/cpl_structure_model.py b/src/cli/cpl/cli/model/cpl_structure_model.py new file mode 100644 index 00000000..3d9434c1 --- /dev/null +++ b/src/cli/cpl/cli/model/cpl_structure_model.py @@ -0,0 +1,146 @@ +import inspect +import json +import os +from inspect import isclass +from pathlib import Path +from typing import Any, Dict, List, Optional, Type, TypeVar + +from cpl.cli.model.cpl_sub_structure_model import CPLSubStructureModel + +T = TypeVar("T", bound="CPLStructureModel") + + +class CPLStructureModel: + def __init__(self, path: Optional[str] = None): + self._path = path + + @property + def path(self) -> Optional[str]: + return self._path + + @classmethod + def from_file(cls: Type[T], path: Path | str) -> T: + if isinstance(path, str): + path = Path(path) + + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + return cls.from_json(data, path=path) + + @classmethod + def from_json(cls: Type[T], data: Dict[str, Any], path: Optional[Path | str] = None) -> T: + if isinstance(path, str): + path = Path(path) + + sig = inspect.signature(cls.__init__) + kwargs: Dict[str, Any] = {} + for name, param in list(sig.parameters.items())[1:]: + if name == "path": + kwargs[name] = str(path) + continue + + if isclass(param.annotation) and issubclass(param.annotation, CPLSubStructureModel): + kwargs[name] = param.annotation.from_json(data[name]) + continue + + if name in data: + kwargs[name] = data[name] + continue + + priv = "_" + name + if priv in data: + kwargs[name] = data[priv] + continue + + camel = _self_or_cls_snake_to_camel(name) + if camel in data: + kwargs[name] = data[camel] + continue + + if param.default is not inspect._empty: + kwargs[name] = param.default + continue + + raise KeyError(f"Missing required field '{name}' for {cls.__name__}.") + + return cls(**kwargs) + + def to_json(self) -> Dict[str, Any]: + result: Dict[str, Any] = {} + for key, value in self.__dict__.items(): + if not key.startswith("_") or key == "_path": + continue + out_key = _self_or_cls_snake_to_camel(key[1:]) + + if isinstance(value, CPLSubStructureModel): + value = value.to_json() + + result[out_key] = value + return result + + def save(self): + if not self._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: + json.dump(self.to_json(), f, indent=2) + + @staticmethod + def _require_str(value, field: str, allow_empty: bool = True) -> str: + if not isinstance(value, str): + raise TypeError(f"{field} must be of type str") + if not allow_empty and not value.strip(): + raise ValueError(f"{field} must not be empty") + return value + + @staticmethod + def _require_optional_non_empty_str(value, field: str) -> Optional[str]: + if value is None: + return None + if not isinstance(value, str): + raise TypeError(f"{field} must be str or None") + s = value.strip() + if not s: + raise ValueError(f"{field} must not be empty when set") + return s + + @staticmethod + def _require_list_of_str(value, field: str) -> List[str]: + if not isinstance(value, list): + raise TypeError(f"{field} must be a list") + out: List[str] = [] + for i, v in enumerate(value): + if not isinstance(v, str): + raise TypeError(f"{field}[{i}] must be of type str") + s = v.strip() + if s: + out.append(s) + + seen = set() + uniq: List[str] = [] + for s in out: + if s not in seen: + seen.add(s) + uniq.append(s) + return uniq + + @staticmethod + def _require_dict_str_str(value, field: str) -> Dict[str, str]: + if not isinstance(value, dict): + raise TypeError(f"{field} must be a dict") + out: Dict[str, str] = {} + for k, v in value.items(): + if not isinstance(k, str) or not k.strip(): + raise TypeError(f"Keys in {field} must be non-empty strings") + if not isinstance(v, str) or not v.strip(): + raise TypeError(f"Values in {field} must be non-empty strings") + out[k.strip()] = v.strip() + return out + + +def _self_or_cls_snake_to_camel(s: str) -> str: + parts = s.split("_") + return parts[0] + "".join(p[:1].upper() + p[1:] for p in parts[1:]) diff --git a/src/cli/cpl/cli/model/cpl_sub_structure_model.py b/src/cli/cpl/cli/model/cpl_sub_structure_model.py new file mode 100644 index 00000000..0f6cf4f5 --- /dev/null +++ b/src/cli/cpl/cli/model/cpl_sub_structure_model.py @@ -0,0 +1,104 @@ +import inspect +import json +from pathlib import Path +from typing import Any, Dict, List, Optional, Type, TypeVar + +T = TypeVar("T", bound="CPLSubStructureModel") + + +class CPLSubStructureModel: + def __init__(self, path: Optional[str] = None): + self._path = path + + @classmethod + def from_json(cls: Type[T], data: Dict[str, Any]) -> T: + sig = inspect.signature(cls.__init__) + kwargs: Dict[str, Any] = {} + for name, param in list(sig.parameters.items())[1:]: + if name in data: + kwargs[name] = data[name] + continue + + priv = "_" + name + if priv in data: + kwargs[name] = data[priv] + continue + + camel = _self_or_cls_snake_to_camel(name) + if camel in data: + kwargs[name] = data[camel] + continue + + if param.default is not inspect._empty: + kwargs[name] = param.default + continue + + raise KeyError(f"Missing required field '{name}' for {cls.__name__}.") + + return cls(**kwargs) + + def to_json(self) -> Dict[str, Any]: + result: Dict[str, Any] = {} + for key, value in self.__dict__.items(): + if not key.startswith("_") or key == "_path": + continue + out_key = _self_or_cls_snake_to_camel(key[1:]) + result[out_key] = value + return result + + @staticmethod + def _require_str(value, field: str, allow_empty: bool = True) -> str: + if not isinstance(value, str): + raise TypeError(f"{field} must be of type str") + if not allow_empty and not value.strip(): + raise ValueError(f"{field} must not be empty") + return value + + @staticmethod + def _require_optional_non_empty_str(value, field: str) -> Optional[str]: + if value is None: + return None + if not isinstance(value, str): + raise TypeError(f"{field} must be str or None") + s = value.strip() + if not s: + raise ValueError(f"{field} must not be empty when set") + return s + + @staticmethod + def _require_list_of_str(value, field: str) -> List[str]: + if not isinstance(value, list): + raise TypeError(f"{field} must be a list") + out: List[str] = [] + for i, v in enumerate(value): + if not isinstance(v, str): + raise TypeError(f"{field}[{i}] must be of type str") + s = v.strip() + if s: + out.append(s) + + seen = set() + uniq: List[str] = [] + for s in out: + if s not in seen: + seen.add(s) + uniq.append(s) + return uniq + + @staticmethod + def _require_dict_str_str(value, field: str) -> Dict[str, str]: + if not isinstance(value, dict): + raise TypeError(f"{field} must be a dict") + out: Dict[str, str] = {} + for k, v in value.items(): + if not isinstance(k, str) or not k.strip(): + raise TypeError(f"Keys in {field} must be non-empty strings") + if not isinstance(v, str) or not v.strip(): + raise TypeError(f"Values in {field} must be non-empty strings") + out[k.strip()] = v.strip() + return out + + +def _self_or_cls_snake_to_camel(s: str) -> str: + parts = s.split("_") + return parts[0] + "".join(p[:1].upper() + p[1:] for p in parts[1:]) diff --git a/src/cli/cpl/cli/model/project.py b/src/cli/cpl/cli/model/project.py new file mode 100644 index 00000000..d89fda8e --- /dev/null +++ b/src/cli/cpl/cli/model/project.py @@ -0,0 +1,283 @@ +import fnmatch +import os +import re +import shutil +from pathlib import Path +from typing import Optional, List, Dict, Self +from urllib.parse import urlparse + +from cpl.cli.model.build import Build +from cpl.cli.model.cpl_structure_model import CPLStructureModel +from cpl.core.console import Console + + +class Project(CPLStructureModel): + _ALLOWED_TYPES = {"application", "library"} + _SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$") + + @staticmethod + def new(path: str, name: str, project_type: str) -> "Project": + return Project( + path, + name, + "0.1.0", + project_type, + "", + "", + "", + "", + [], + {}, + {}, + [], + None, + "./", + Build.new([], []), + ) + + def __init__( + self, + path: str, + name: str, + version: str, + type: str, + license: str, + author: str, + description: str, + homepage: str, + keywords: List[str], + dependencies: Dict[str, str], + dev_dependencies: Dict[str, str], + references: List[str], + main: Optional[str], + directory: str, + build: Build, + ): + CPLStructureModel.__init__(self, path) + + self._name = name + self._version = version + self._type = type + self._license = license + self._author = author + self._description = description + self._homepage = homepage + self._keywords = keywords + self._dependencies = dependencies + self._dev_dependencies = dev_dependencies + self._references = references + self._main = main + self._directory = directory + self._build = build + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, value: str): + self._name = self._require_str(value, "name", allow_empty=False).strip() + + @property + def version(self) -> str: + return self._version + + @version.setter + def version(self, value: str): + value = self._require_str(value, "version", allow_empty=False).strip() + if not self._SEMVER_RE.match(value): + raise ValueError("version must follow SemVer X.Y.Z (optionally with -/+)") + self._version = value + + @property + def type(self) -> str: + return self._type + + @type.setter + def type(self, value: str): + value = self._require_str(value, "type", allow_empty=False).strip() + if value not in self._ALLOWED_TYPES: + allowed = ", ".join(sorted(self._ALLOWED_TYPES)) + raise ValueError(f"type must be one of: {allowed}") + self._type = value + + @property + def license(self) -> str: + return self._license + + @license.setter + def license(self, value: str): + self._license = self._require_str(value, "license", allow_empty=True).strip() + + @property + def author(self) -> str: + return self._author + + @author.setter + def author(self, value: str): + self._author = self._require_str(value, "author", allow_empty=True).strip() + + @property + def description(self) -> str: + return self._description + + @description.setter + def description(self, value: str): + self._description = self._require_str(value, "description", allow_empty=True).strip() + + @property + def homepage(self) -> str: + return self._homepage + + @homepage.setter + def homepage(self, value: str): + value = self._require_str(value, "homepage", allow_empty=True).strip() + if value: + parsed = urlparse(value) + if parsed.scheme not in ("http", "https") or not parsed.netloc: + raise ValueError("homepage must be a valid http/https URL") + self._homepage = value + + @property + def keywords(self) -> List[str]: + return self._keywords + + @keywords.setter + def keywords(self, value: List[str]): + self._keywords = self._require_list_of_str(value, "keywords") + + @property + def dependencies(self) -> Dict[str, str]: + return self._dependencies + + @dependencies.setter + def dependencies(self, value: Dict[str, str]): + self._dependencies = self._require_dict_str_str(value, "dependencies") + + @property + def dev_dependencies(self) -> Dict[str, str]: + return self._dev_dependencies + + @dev_dependencies.setter + def dev_dependencies(self, value: Dict[str, str]): + self._dev_dependencies = self._require_dict_str_str(value, "devDependencies") + + @property + def references(self) -> List[str]: + return self._references + + @references.setter + def references(self, value: List[str]): + self._references = self._require_list_of_str(value, "references") + + @property + def main(self) -> Optional[str]: + return self._main + + @main.setter + def main(self, value: Optional[str]): + self._main = self._require_optional_non_empty_str(value, "main") + + @property + def directory(self) -> str: + return self._directory + + @directory.setter + def directory(self, value: str): + self._directory = self._require_str(value, "directory", allow_empty=False).strip() + + @property + def build(self) -> Build: + return self._build + + def _collect_files(self, rel_dir: Path) -> List[Path]: + files: List[Path] = [] + 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): + root_path = Path(root) + rel_root = root_path.relative_to(rel_dir).as_posix() + + dirnames[:] = [ + d + for d in dirnames + if not any( + fnmatch.fnmatch(f"{rel_root}/{d}" if rel_root else d, pattern) or fnmatch.fnmatch(d, pattern) + for pattern in exclude_patterns + ) + ] + + for filename in filenames: + rel_path = f"{rel_root}/{filename}" if rel_root else filename + if any( + fnmatch.fnmatch(rel_path, pattern) or fnmatch.fnmatch(filename, pattern) + for pattern in exclude_patterns + ): + continue + + files.append(root_path / filename) + + return files + + def build_references(self, dist: Path, verbose: bool = False): + references = [] + old_dir = os.getcwd() + os.chdir(Path(self.path).parent) + for ref in self.references: + os.chdir(Path(ref).parent) + references.append(Project.from_file(ref)) + + for p in references: + os.chdir(Path(p.path).parent) + p.do_build(dist, verbose, self) + + os.chdir(old_dir) + + def do_build(self, dist: Path, verbose: bool = False, parent: Self = None, silent: bool = False): + if isinstance(dist, str): + dist = Path(dist) + + dist_project = self if parent is None else parent + dist_path = (dist / dist_project.name / self.directory).resolve().absolute() + + if parent is None: + if verbose: + Console.write_line(f" Cleaning dist folder at {dist_path}...") + shutil.rmtree(str(dist_path), ignore_errors=True) + + if verbose: + Console.write_line(f" Building references for project {self.name}...") + + self.build_references(dist, verbose) + + def _build(): + if verbose: + Console.write_line(f" Collecting project '{self.name}' files...") + + rel_dir = (Path(self.path).parent / Path(self.directory)).absolute() + files = self._collect_files(rel_dir) + if len(files) == 0: + if verbose: + Console.write_line(f" No files found in {rel_dir}, skipping copy.") + return + + for file in files: + rel_path = file.relative_to(rel_dir) + dest_file_path = dist_path / rel_path + + if not dest_file_path.parent.exists(): + os.makedirs(dest_file_path.parent, exist_ok=True) + + shutil.copy(file, dest_file_path) + + if verbose: + Console.write_line(f" Copied {len(files)} files from {rel_dir} to {dist_path}") + + if not silent: + Console.write_line(" Done!") + + if silent: + _build() + return + Console.spinner(f"Building project {self.name}...", lambda: _build()) diff --git a/src/cli/cpl/cli/model/workspace.py b/src/cli/cpl/cli/model/workspace.py new file mode 100644 index 00000000..a80c64d7 --- /dev/null +++ b/src/cli/cpl/cli/model/workspace.py @@ -0,0 +1,77 @@ +from typing import Optional, List, Dict + +from cpl.cli.model.cpl_structure_model import CPLStructureModel +from cpl.cli.model.project import Project + + +class Workspace(CPLStructureModel): + @staticmethod + def new(path: str, name: str) -> "Workspace": + return Workspace( + path=path, + name=name, + projects=[], + default_project=None, + scripts={}, + ) + + def __init__( + self, + path: str, + name: str, + projects: List[str], + default_project: Optional[str], + scripts: Dict[str, str], + ): + CPLStructureModel.__init__(self, path) + + self._name = name + self._projects = projects + self._default_project = default_project + self._scripts = scripts + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, value: str): + self._name = self._require_str(value, "name", allow_empty=False).strip() + + @property + def projects(self) -> List[str]: + return self._projects + + @projects.setter + def projects(self, value: List[str]): + self._projects = self._require_list_of_str(value, "projects") + + @property + def actual_projects(self) -> List[Project]: + return [Project.from_file(p) for p in self._projects] + + @property + def project_names(self) -> List[str]: + return [Project.from_file(p).name for p in self._projects] + + @property + def default_project(self) -> Optional[str]: + return self._default_project + + @default_project.setter + def default_project(self, value: Optional[str]): + self._default_project = self._require_optional_non_empty_str(value, "defaultProject") + + @property + def scripts(self) -> Dict[str, str]: + return self._scripts + + @scripts.setter + def scripts(self, value: Dict[str, str]): + self._scripts = self._require_dict_str_str(value, "scripts") + + def get_project_by_name(self, name: str) -> Optional[Project]: + for project in self.actual_projects: + if project.name == name: + return project + return None diff --git a/src/cpl-graphql/cpl/graphql/abc/__init__.py b/src/cli/cpl/cli/utils/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/abc/__init__.py rename to src/cli/cpl/cli/utils/__init__.py diff --git a/src/cli/cpl/cli/utils/custom_command.py b/src/cli/cpl/cli/utils/custom_command.py new file mode 100644 index 00000000..866fd186 --- /dev/null +++ b/src/cli/cpl/cli/utils/custom_command.py @@ -0,0 +1,18 @@ +import subprocess + +import click + + +def script_command(cli_group, name, command): + + @cli_group.command(name) + @click.argument("args", nargs=-1) + def _run_script(args): + click.echo(f"Running script: {name}") + try: + cmd = command.split() + list(args) + subprocess.run(cmd, check=True) + except subprocess.CalledProcessError as e: + click.echo(f"Script '{name}' failed with exit code {e.returncode}", err=True) + except FileNotFoundError: + click.echo(f"Command not found: {command.split()[0]}", err=True) diff --git a/src/cli/cpl/cli/utils/json.py b/src/cli/cpl/cli/utils/json.py new file mode 100644 index 00000000..f39e0400 --- /dev/null +++ b/src/cli/cpl/cli/utils/json.py @@ -0,0 +1,14 @@ +import json +from pathlib import Path + + +def load_project_json(path: Path) -> dict: + if not path.exists(): + return {} + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + + +def save_project_json(path: Path, data: dict): + with open(path, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) diff --git a/src/cpl-graphql/cpl/graphql/auth/__init__.py b/src/cli/cpl/cli/utils/live_server/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/__init__.py rename to src/cli/cpl/cli/utils/live_server/__init__.py diff --git a/src/cli/cpl/cli/utils/live_server/file_event_handler.py b/src/cli/cpl/cli/utils/live_server/file_event_handler.py new file mode 100644 index 00000000..009a0b7b --- /dev/null +++ b/src/cli/cpl/cli/utils/live_server/file_event_handler.py @@ -0,0 +1,51 @@ +import re +import time + +from watchdog.events import FileSystemEventHandler, FileModifiedEvent, FileClosedEvent + + +class FileEventHandler(FileSystemEventHandler): + + _ignore_patterns = [ + r".*~$", + r".*\.swp$", + r".*\.swx$", + r".*\.tmp$", + r".*__pycache__.*", + r".*\.pytest_cache.*", + r".*/\.idea/.*", + r".*/\.vscode/.*", + r".*\.DS_Store$", + r"#.*#$", + ] + _watch_extensions = (".py", ".json", ".yaml", ".yml", ".toml") + + def __init__(self, on_save, debounce_seconds: float = 0.5): + super().__init__() + self._on_save = on_save + self._debounce = debounce_seconds + self._last_triggered = 0 + self._last_file = None + + def _should_ignore(self, path: str) -> bool: + for pattern in self._ignore_patterns: + if re.match(pattern, path): + return True + return not path.endswith(self._watch_extensions) + + def _debounced_trigger(self, path: str): + now = time.time() + if path != self._last_file or now - self._last_triggered > self._debounce: + self._last_triggered = now + self._last_file = path + self._on_save(path) + + def on_modified(self, event): + if not event.is_directory and isinstance(event, FileModifiedEvent): + if not self._should_ignore(event.src_path): + self._debounced_trigger(event.src_path) + + def on_closed(self, event): + if not event.is_directory and isinstance(event, FileClosedEvent): + if not self._should_ignore(event.src_path): + self._debounced_trigger(event.src_path) diff --git a/src/cli/cpl/cli/utils/live_server/live_server.py b/src/cli/cpl/cli/utils/live_server/live_server.py new file mode 100644 index 00000000..acb98172 --- /dev/null +++ b/src/cli/cpl/cli/utils/live_server/live_server.py @@ -0,0 +1,103 @@ +import os +import subprocess +import time +from pathlib import Path + +from watchdog.observers import Observer + +from cpl.cli.model.project import Project +from cpl.cli.utils.live_server.file_event_handler import FileEventHandler +from cpl.cli.utils.venv import get_venv_python, ensure_venv +from cpl.core.configuration import Configuration +from cpl.core.console import Console + + +class LiveServer: + def __init__(self, project: Project, args: list[str], dev: bool = False, verbose: bool = False): + path = str(Path(project.path).parent.resolve().absolute()) + executable = (Path(project.path).parent / Path(project.directory) / project.main).resolve().absolute() + + self._dist_path = None + if not dev: + self._dist_path = Path(project.path).parent / "dist" + + if Configuration.get("workspace") is not None: + self._dist_path = Path(Configuration.get("workspace").path).parent / "dist" + + self._dist_path = Path(self._dist_path).resolve().absolute() + if verbose: + Console.write_line(f"Creating dist folder at {self._dist_path}...") + + os.makedirs(self._dist_path, exist_ok=True) + project.do_build(self._dist_path, verbose) + path = self._dist_path / project.name / project.directory + main = project.main.replace(project.directory, "").lstrip("/\\") + + executable = (path / main).resolve().absolute() + + if not os.path.isfile(executable): + Console.error(f"Executable {executable} not found.") + return + + self._project = project + self._sources = (Path(project.path).parent / Path(project.directory)).resolve().absolute() + self._executable = executable + self._working_directory = Path(path) + self._args = args + self._is_dev = dev + self._verbose = verbose + + self._process = None + self._observer = None + self._python = str(get_venv_python(ensure_venv(Path("./"))).absolute()) + + def _run_executable(self): + if self._process: + self._process.terminate() + self._process.wait() + + self._process = subprocess.Popen( + [self._python, str(self._executable), *self._args], + cwd=self._working_directory, + ) + + def _on_change(self, changed_file: str): + if self._verbose: + Console.write_line(f"Change detected: {changed_file}") + + if self._is_dev and self._process: + self._process.terminate() + self._process.wait() + + Console.write_line("Restart\n\n") + time.sleep(0.5) # debounce to avoid copy temp files + if not self._is_dev: + self._project.do_build(self._dist_path, verbose=self._verbose, silent=True) + + self._run_executable() + + def start(self): + handler = FileEventHandler(self._on_change) + observer = Observer() + observer.schedule(handler, str(self._sources), recursive=True) + observer.start() + self._observer = observer + + Console.write_line("** CPL live development server is running **\n") + Console.write_line(f"Watching {self._sources} ... (Ctrl+C to stop)") + Console.write_line(f"Starting {self._executable} ...\n\n") + self._run_executable() + + try: + while True: + time.sleep(1) + except KeyboardInterrupt as e: + time.sleep(1) + Console.write_line("Stopping...") + finally: + if self._process: + self._process.terminate() + self._process.wait() + observer.stop() + observer.join() + Console.close() diff --git a/src/cli/cpl/cli/utils/name-utils.py b/src/cli/cpl/cli/utils/name-utils.py new file mode 100644 index 00000000..3d41459f --- /dev/null +++ b/src/cli/cpl/cli/utils/name-utils.py @@ -0,0 +1,13 @@ +class NameUtils: + @staticmethod + def classify(name: str) -> str: # UserService + return "".join(w.capitalize() for w in name.replace("-", "_").split("_")) + + @staticmethod + def dasherize(name: str) -> str: # user-service + return name.replace("_", "-").lower() + + @staticmethod + def camelize(name: str) -> str: # userService + parts = name.split("-") + return parts[0] + "".join(w.capitalize() for w in parts[1:]) diff --git a/src/cli/cpl/cli/utils/pip.py b/src/cli/cpl/cli/utils/pip.py new file mode 100644 index 00000000..3b187134 --- /dev/null +++ b/src/cli/cpl/cli/utils/pip.py @@ -0,0 +1,177 @@ +import os.path +import re +import subprocess +from pathlib import Path + +from cpl.cli.utils.venv import ensure_venv, get_venv_pip +from cpl.core.console import Console + + +class Pip: + _ANY_PREFIX_RE = re.compile(r"(===|~=|==|!=|>=|<=|>>|<<|>|<|!|~|=|\^)") + + @staticmethod + def normalize_dep(name: str, raw: str) -> str: + raw = raw.strip() + if not raw: + return name + + table = { + "!": "!=", + ">": ">=", + ">>": ">", + "<": "<=", + "<<": "<", + "~": "~=", + "=": "===", + } + + op = "==" + for prefix, pip_op in table.items(): + if raw.startswith(prefix): + op = pip_op + raw = raw[len(prefix) :] + break + + return f"{name}{op}{raw}" + + @classmethod + def apply_prefix(cls, installed: str, spec: str = None) -> str: + if spec is None or not spec.strip(): + return f"~{installed}" + + s = spec.strip() + if "," in s: + return s + + m = cls._ANY_PREFIX_RE.search(s) + if not m: + return f"~{installed}" + + op = m.group(1) + rest = s[m.end() :].strip() + if "," in rest: + rest = rest.split(",", 1)[0].strip() + if " " in rest: + rest = rest.split()[0] + + orig_version = rest + + installed_parts = [p for p in installed.split(".") if p != ""] + if orig_version: + orig_parts = [p for p in orig_version.split(".") if p != ""] + trimmed_installed = ".".join(installed_parts[: len(orig_parts)]) or installed + else: + trimmed_installed = installed + + pip_to_cpl = { + "==": "", + "!=": "!", + ">=": ">", + ">": ">>", + "<=": "<", + "<": "<<", + "~=": "~", + "===": "=", + "^": "~", + } + + if op in pip_to_cpl: + cpl_op = pip_to_cpl[op] + else: + cpl_op = op + + return f"{cpl_op}{trimmed_installed}" + + @classmethod + def get_package_without_version(cls, spec: str) -> str: + for sep in ["==", ">=", "<=", ">", "<", "~=", "!="]: + if sep in spec: + return spec.split(sep, 1)[0].strip() or None + + return spec.strip() or None + + @classmethod + def get_package_full_version(cls, spec: str) -> str | None: + package_name = cls.get_package_without_version(spec) + return spec.replace(package_name, "").strip() or None + + @staticmethod + def command(command: str, *args, verbose: bool = False, path: Path = None): + if path is not None and path.is_file(): + path = os.path.dirname(path) + + venv_path = ensure_venv(Path(os.getcwd()) / Path(path or "./")) + pip = get_venv_pip(venv_path) + if verbose: + Console.write_line() + Console.write_line(f"Running: {pip} {command} {''.join(args)}") + + subprocess.run( + [*pip.split(), *command.split(), *args], + check=True, + cwd=path, + stdin=subprocess.DEVNULL if not verbose else None, + stdout=subprocess.DEVNULL if not verbose else None, + stderr=subprocess.DEVNULL if not verbose else None, + ) + + @staticmethod + def get_package_version(package: str, path: str = None) -> str | None: + venv_path = ensure_venv(Path(path or "./")) + pip = get_venv_pip(venv_path) + + try: + result = subprocess.run( + [*pip.split(), "show", package], + check=True, + capture_output=True, + text=True, + stdin=subprocess.DEVNULL, + ) + for line in result.stdout.splitlines(): + if line.startswith("Version:"): + return line.split(":", 1)[1].strip() + except subprocess.CalledProcessError: + return None + + return None + + @staticmethod + def get_packages(path: str = None): + venv_path = ensure_venv(Path(path or "./")) + pip = get_venv_pip(venv_path) + try: + result = subprocess.run( + [*pip.split(), "list", "--format=freeze"], + check=True, + capture_output=True, + text=True, + stdin=subprocess.DEVNULL, + ) + packages = {} + for line in result.stdout.splitlines(): + if "==" in line: + name, version = line.split("==", 1) + packages[name] = version + return packages + except subprocess.CalledProcessError: + return {} + + @staticmethod + def get_pip_version(path: str = None) -> str | None: + venv_path = ensure_venv(Path(path or "./")) + pip = get_venv_pip(venv_path) + + try: + result = subprocess.run( + [*pip.split(), "--version"], + check=True, + capture_output=True, + text=True, + stdin=subprocess.DEVNULL, + ) + version = result.stdout.split()[1] + return version + except subprocess.CalledProcessError: + return None diff --git a/src/cli/cpl/cli/utils/prompt.py b/src/cli/cpl/cli/utils/prompt.py new file mode 100644 index 00000000..d491f613 --- /dev/null +++ b/src/cli/cpl/cli/utils/prompt.py @@ -0,0 +1,28 @@ +import click + +from cpl.cli.const import PROJECT_TYPES + + +class SmartChoice(click.Choice): + + def __init__(self, choices: list, aliases: dict = None): + click.Choice.__init__(self, choices, case_sensitive=False) + + self._aliases = {c: c[0].lower() for c in choices if len(c) > 0} + if aliases: + self._aliases.update({k: v.lower() for k, v in aliases.items()}) + + if any([x[0].lower in self._aliases for x in choices if len(x) > 1]): + raise ValueError("Alias conflict with first letters of choices") + + def convert(self, value, param, ctx): + val_lower = value.lower() + if val_lower in self._aliases.values(): + value = [k for k, v in self._aliases.items() if v == val_lower][0] + return super().convert(value, param, ctx) + + def get_metavar(self, param, ctx): + return "|".join([f"({a}){option}" for option, a in self._aliases.items()]) + + +ProjectType = SmartChoice(["workspace"] + PROJECT_TYPES, {"workspace": "ws"}) diff --git a/src/cli/cpl/cli/utils/structure.py b/src/cli/cpl/cli/utils/structure.py new file mode 100644 index 00000000..1a2116da --- /dev/null +++ b/src/cli/cpl/cli/utils/structure.py @@ -0,0 +1,202 @@ +import os +import shutil +import textwrap +from pathlib import Path + +import click + +from cpl import cli +from cpl.cli.model.project import Project +from cpl.cli.model.workspace import Workspace +from cpl.cli.utils.template_renderer import TemplateRenderer +from cpl.core.console import Console + + +class Structure: + _dependency_map = { + "console": [ + "cpl-core", + ], + "web": [ + "cpl-api", + ], + "graphql": [ + "cpl-graphql", + ], + "library": [ + "cpl-core", + ], + "service": [ + "cpl-core", + ], + } + + @staticmethod + def find_workspace_in_path(path: Path, with_parents=False) -> Workspace | None: + current_path = path.resolve() + paths = [current_path] + if with_parents: + paths.extend(current_path.parents) + + for parent in paths: + workspace_file = parent / "cpl.workspace.json" + if workspace_file.exists() and workspace_file.is_file(): + ws = Workspace.from_file(workspace_file) + return ws + + return None + + @staticmethod + def create_pyproject_toml(project: Project, path: Path): + pyproject_path = path / "pyproject.toml" + if pyproject_path.exists(): + return + + content = textwrap.dedent( + f""" + [build-system] + requires = ["setuptools>=70.1.0", "wheel", "build"] + build-backend = "setuptools.build_meta" + [project] + name = "{project.name}" + version = "{project.version or '0.1.0'}" + description = "{project.description or ''}" + authors = [{{name="{project.author or ''}"}}] + license = "{project.license or ''}" + dependencies = [{', '.join([f'"{dep}"' for dep in project.dependencies])}] + """ + ).lstrip() + + 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.") + + 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}'") + return workspace + + @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.error("cpl.project.json already exists.") + raise SystemExit(1) + + 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() + + from cpl.cli.command.package.install import install + + old_cwd = os.getcwd() + os.chdir(Path(workspace.path).parent) + install.callback(f"cpl-cli>={cli.__version__}", project.name, dev=True, verbose=verbose) + if project_type in Structure._dependency_map: + for package in Structure._dependency_map[project_type]: + install.callback(package, project.name, dev=False, verbose=verbose) + + os.chdir(old_cwd) + + 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}'") + return project + + @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) diff --git a/src/cli/cpl/cli/utils/template_collector.py b/src/cli/cpl/cli/utils/template_collector.py new file mode 100644 index 00000000..6de54115 --- /dev/null +++ b/src/cli/cpl/cli/utils/template_collector.py @@ -0,0 +1,41 @@ +import os +from pathlib import Path + +from cpl.core.console import Console + + +class TemplateCollector: + _templates = {} + _collected_paths = [] + + @classmethod + def get_templates(cls) -> dict[str, str]: + return cls._templates + + @classmethod + def collect_templates(cls, directory: Path, verbose=False): + if not directory.exists() or not directory.is_dir(): + raise FileNotFoundError(f"Directory '{directory}' does not exist") + + if not str(directory).endswith(".cpl/generate"): + directory = directory / ".cpl" / "generate" + + directory = directory.resolve().absolute() + + if directory in cls._collected_paths: + return + + cls._collected_paths.append(directory) + if not directory.exists() or not directory.is_dir(): + if verbose: + Console.write_line(f"No templates found in {directory}") + return + + templates = {} + for root, _, files in os.walk(directory): + for file in files: + if file.endswith(".schematic"): + with open(os.path.join(root, file), "r") as f: + templates[os.path.relpath(os.path.join(root, file), directory)] = f.read() + + cls._templates.update(templates) diff --git a/src/cli/cpl/cli/utils/template_renderer.py b/src/cli/cpl/cli/utils/template_renderer.py new file mode 100644 index 00000000..a3a5afc2 --- /dev/null +++ b/src/cli/cpl/cli/utils/template_renderer.py @@ -0,0 +1,23 @@ +from cpl.core.utils import String + + +class TemplateRenderer: + + @staticmethod + def render_template(schematic, template_str: str, name: str, path: str) -> str: + context = { + "schematic": schematic, + "Name": String.to_pascal_case(name), + "name": String.to_snake_case(name), + "NAME": String.to_snake_case(name).upper(), + "camelName": String.to_camel_case(name), + "multi_Name": f"{String.to_pascal_case(name)}s", + "multi_name": f"{String.to_snake_case(name)}s", + "multi_NAME": f"{String.to_snake_case(name).upper()}s", + "multi_camelName": f"{String.to_camel_case(name)}s", + "path": path.replace("\\", "/"), + } + + for key, value in context.items(): + template_str = template_str.replace(f"<{key}>", value) + return template_str diff --git a/src/cli/cpl/cli/utils/venv.py b/src/cli/cpl/cli/utils/venv.py new file mode 100644 index 00000000..497e53bf --- /dev/null +++ b/src/cli/cpl/cli/utils/venv.py @@ -0,0 +1,46 @@ +import os +import sys +import venv +from pathlib import Path + +from cpl.core.configuration import Configuration +from cpl.core.console import Console + + +def ensure_venv(start_path: Path | None = None) -> Path: + start_path = start_path or Path.cwd() + from cpl.cli.utils.structure import Structure + + workspace = Structure.find_workspace_in_path(start_path) + + if workspace is not None: + workspace = Path(os.path.dirname(workspace.path)) + + ws_venv = workspace / ".venv" + if ws_venv.exists(): + return ws_venv + + for parent in [start_path, *start_path.parents]: + venv_path = parent / ".venv" + if venv_path.exists(): + return venv_path + + if workspace is not None: + venv_path = workspace / ".venv" + else: + venv_path = start_path / ".venv" + + Console.write_line(f"Creating virtual environment at {venv_path.resolve().absolute()}...") + venv.EnvBuilder(with_pip=True).create(venv_path) + return venv_path + + +def get_venv_python(venv_path: Path) -> Path: + if sys.platform == "win32": + return venv_path / "Scripts" / "python.exe" + return venv_path / "bin" / "python" + + +def get_venv_pip(venv_path: Path) -> str: + python_exe = get_venv_python(venv_path) + return f"{python_exe} -m pip" diff --git a/src/cli/pyproject.toml b/src/cli/pyproject.toml new file mode 100644 index 00000000..92523473 --- /dev/null +++ b/src/cli/pyproject.toml @@ -0,0 +1,32 @@ +[build-system] +requires = ["setuptools>=70.1.0", "wheel>=0.43.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "cpl-cli" +version = "2024.7.0" +description = "CPL cli" +readme = "CPL cli package" +requires-python = ">=3.12" +license = "MIT" +authors = [ + { name = "Sven Heidemann", email = "sven.heidemann@sh-edraft.de" } +] +keywords = ["cpl", "cli", "backend", "shared", "library"] + +dynamic = ["dependencies", "optional-dependencies"] + +[project.scripts] +cpl = "cpl.cli.main:main" + +[project.urls] +Homepage = "https://www.sh-edraft.de" + +[tool.setuptools.packages.find] +where = ["."] +include = ["cpl*"] + +[tool.setuptools.dynamic] +dependencies = { file = ["requirements.txt"] } +optional-dependencies.dev = { file = ["requirements.dev.txt"] } + diff --git a/src/cpl-core/requirements.dev.txt b/src/cli/requirements.dev.txt similarity index 100% rename from src/cpl-core/requirements.dev.txt rename to src/cli/requirements.dev.txt diff --git a/src/cli/requirements.txt b/src/cli/requirements.txt new file mode 100644 index 00000000..06b0dafe --- /dev/null +++ b/src/cli/requirements.txt @@ -0,0 +1,3 @@ +cpl-core +click==8.3.0 +watchdog==6.0.0 \ No newline at end of file diff --git a/src/cli/run b/src/cli/run new file mode 100755 index 00000000..cc22a471 --- /dev/null +++ b/src/cli/run @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +cd ../ +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +py_list="$ROOT_DIR" +for d in "$ROOT_DIR"/*; do + [ -d "$d" ] || continue + py_list="$py_list:$d" +done +export PYTHONPATH="${py_list}${PYTHONPATH:+:$PYTHONPATH}" + +old_dir="$(pwd)" +cd ../ +python -m cpl.cli.main "$@" +cd "$old_dir" \ No newline at end of file diff --git a/src/core/cpl.project.json b/src/core/cpl.project.json new file mode 100644 index 00000000..5cebf59d --- /dev/null +++ b/src/core/cpl.project.json @@ -0,0 +1,32 @@ +{ + "name": "cpl-core", + "version": "0.1.0", + "type": "library", + "license": "MIT", + "author": "Sven Heidemann", + "description": "CLI for the CPL library", + "homepage": "", + "keywords": [], + "dependencies": { + "art": "~6.5", + "colorama": "~0.4.6", + "tabulate": "~0.9.0", + "termcolor": "~3.1.0", + "pynput": "~1.8.1", + "croniter": "~6.0.0" + }, + "devDependencies": { + "black": "~25.9" + }, + "references": [], + "main": null, + "directory": "cpl/core", + "build": { + "include": [], + "exclude": [ + "**/__pycache__", + "**/logs", + "**/tests" + ] + } +} \ No newline at end of file diff --git a/src/core/cpl/core/__init__.py b/src/core/cpl/core/__init__.py new file mode 100644 index 00000000..5becc17c --- /dev/null +++ b/src/core/cpl/core/__init__.py @@ -0,0 +1 @@ +__version__ = "1.0.0" diff --git a/src/cpl-graphql/cpl/graphql/auth/api_key/__init__.py b/src/core/cpl/core/abc/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/api_key/__init__.py rename to src/core/cpl/core/abc/__init__.py diff --git a/src/cpl-core/cpl/core/abc/registry_abc.py b/src/core/cpl/core/abc/registry_abc.py similarity index 100% rename from src/cpl-core/cpl/core/abc/registry_abc.py rename to src/core/cpl/core/abc/registry_abc.py diff --git a/src/cpl-core/cpl/core/configuration/__init__.py b/src/core/cpl/core/configuration/__init__.py similarity index 100% rename from src/cpl-core/cpl/core/configuration/__init__.py rename to src/core/cpl/core/configuration/__init__.py diff --git a/src/cpl-core/cpl/core/configuration/configuration.py b/src/core/cpl/core/configuration/configuration.py similarity index 99% rename from src/cpl-core/cpl/core/configuration/configuration.py rename to src/core/cpl/core/configuration/configuration.py index 5963d6db..e4ca5053 100644 --- a/src/cpl-core/cpl/core/configuration/configuration.py +++ b/src/core/cpl/core/configuration/configuration.py @@ -133,5 +133,4 @@ class Configuration: if isclass(key) and issubclass(key, ConfigurationModelABC) and result == default: result = key() cls.set(key, result) - return result diff --git a/src/cpl-core/cpl/core/configuration/configuration_model_abc.py b/src/core/cpl/core/configuration/configuration_model_abc.py similarity index 100% rename from src/cpl-core/cpl/core/configuration/configuration_model_abc.py rename to src/core/cpl/core/configuration/configuration_model_abc.py diff --git a/src/cpl-core/cpl/core/console/__init__.py b/src/core/cpl/core/console/__init__.py similarity index 100% rename from src/cpl-core/cpl/core/console/__init__.py rename to src/core/cpl/core/console/__init__.py diff --git a/src/cpl-core/cpl/core/console/_call.py b/src/core/cpl/core/console/_call.py similarity index 100% rename from src/cpl-core/cpl/core/console/_call.py rename to src/core/cpl/core/console/_call.py diff --git a/src/cpl-core/cpl/core/console/_spinner.py b/src/core/cpl/core/console/_spinner.py similarity index 66% rename from src/cpl-core/cpl/core/console/_spinner.py rename to src/core/cpl/core/console/_spinner.py index 74fab96a..1dcdf301 100644 --- a/src/cpl-core/cpl/core/console/_spinner.py +++ b/src/core/cpl/core/console/_spinner.py @@ -1,6 +1,5 @@ -import os +import shutil import sys -import multiprocessing import time from multiprocessing import Process @@ -20,18 +19,28 @@ class Spinner(Process): Foreground color of the spinner background_color: :class:`cpl.core.console.background_color.BackgroundColorEnum` Background color of the spinner + done_char: :class:`str` """ - def __init__(self, msg_len: int, foreground_color: ForegroundColorEnum, background_color: BackgroundColorEnum): + def __init__( + self, + foreground_color: ForegroundColorEnum, + background_color: BackgroundColorEnum, + done_char: str = None, + msg_len: int = None, + ): Process.__init__(self) - self._msg_len = msg_len self._foreground_color = foreground_color self._background_color = background_color self._is_spinning = True self._exit = False + assert done_char is None or len(done_char) == 1, "done_char must be a single character" + self._done_char = done_char or "✓" + self._msg_len = msg_len + @staticmethod def _spinner(): r"""Selects active spinner char""" @@ -52,44 +61,33 @@ class Spinner(Process): def run(self) -> None: r"""Entry point of process, shows the spinner""" - columns = 0 - if sys.platform == "win32": - columns = os.get_terminal_size().columns - else: - values = os.popen("stty size", "r").read().split() - term_rows, term_columns = values if len(values) == 2 else (0, 0) - columns = int(term_columns) - - end_msg = "done" - - padding = columns - self._msg_len - len(end_msg) - if padding > 0: - print(f'{"" : >{padding}}', end="") - else: - print("", end="") + size = shutil.get_terminal_size(fallback=(80, 24)) + columns = max(1, size.columns) spinner = self._spinner() + color_args = self._get_color_args() + + if self._msg_len is not None: + columns = min(columns, self._msg_len + 2) + while self._is_spinning: - print(colored(f"{next(spinner): >{len(end_msg)}}", *self._get_color_args()), end="") + frame = next(spinner) + sys.stdout.write(f"\033[{columns}G") + print(colored(frame, *color_args), end="", flush=True) time.sleep(0.1) - back = "" - for i in range(0, len(end_msg)): - back += "\b" - - print(back, end="") + sys.stdout.write(f"\033[{columns}G") sys.stdout.flush() - if not self._exit: - print(colored(end_msg, *self._get_color_args()), end="") - def stop(self): r"""Stops the spinner""" self._is_spinning = False - super().terminate() time.sleep(0.1) + print("\b" + colored(self._done_char, *self._get_color_args()), end="", flush=True) + super().terminate() def exit(self): r"""Stops the spinner""" self._is_spinning = False self._exit = True time.sleep(0.1) + super().terminate() diff --git a/src/cpl-core/cpl/core/console/background_color_enum.py b/src/core/cpl/core/console/background_color_enum.py similarity index 100% rename from src/cpl-core/cpl/core/console/background_color_enum.py rename to src/core/cpl/core/console/background_color_enum.py diff --git a/src/cpl-core/cpl/core/console/console.py b/src/core/cpl/core/console/console.py similarity index 95% rename from src/cpl-core/cpl/core/console/console.py rename to src/core/cpl/core/console/console.py index ce764832..4a4d8620 100644 --- a/src/cpl-core/cpl/core/console/console.py +++ b/src/core/cpl/core/console/console.py @@ -1,4 +1,5 @@ import os +import shutil import sys import time from collections.abc import Callable @@ -71,13 +72,17 @@ class Console: cls._background_color = color @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 Parameter: color: Union[:class:`cpl.core.console.background_color_enum.BackgroundColorEnum`, :class:`str`] Foreground color of the console """ + if color is None: + cls._foreground_color = ForegroundColorEnum.default + return + if type(color) is str: cls._foreground_color = ForegroundColorEnum[color] else: @@ -251,6 +256,25 @@ class Console: Console.read() sys.exit() + @classmethod + def divider(cls, char: str = "-"): + r"""Prints a divider line + + Parameter: + char: :class:`str` + Character to use for the divider + """ + if cls._disabled: + return + + if cls._hold_back: + cls._hold_back_calls.append(ConsoleCall(cls.divider, char)) + return + + size = shutil.get_terminal_size(fallback=(80, 24)) + columns = max(1, size.columns) + cls.write_line(char * columns) + @classmethod def disable(cls): r"""Disables console interaction""" @@ -415,6 +439,8 @@ class Console: message: str, call: Callable, *args, + done_char: str = None, + full_width: bool = False, text_foreground_color: Union[str, ForegroundColorEnum] = None, spinner_foreground_color: Union[str, ForegroundColorEnum] = None, text_background_color: Union[str, BackgroundColorEnum] = None, @@ -464,7 +490,8 @@ class Console: cls.set_hold_back(True) spinner = None if not cls._disabled: - spinner = Spinner(len(message), spinner_foreground_color, spinner_background_color) + msg_len = None if full_width else len(message) + 1 + spinner = Spinner(spinner_foreground_color, spinner_background_color, done_char=done_char, msg_len=msg_len) spinner.start() return_value = None diff --git a/src/cpl-core/cpl/core/console/foreground_color_enum.py b/src/core/cpl/core/console/foreground_color_enum.py similarity index 100% rename from src/cpl-core/cpl/core/console/foreground_color_enum.py rename to src/core/cpl/core/console/foreground_color_enum.py diff --git a/src/cpl-core/cpl/core/ctx/__init__.py b/src/core/cpl/core/ctx/__init__.py similarity index 100% rename from src/cpl-core/cpl/core/ctx/__init__.py rename to src/core/cpl/core/ctx/__init__.py diff --git a/src/cpl-core/cpl/core/ctx/user_context.py b/src/core/cpl/core/ctx/user_context.py similarity index 100% rename from src/cpl-core/cpl/core/ctx/user_context.py rename to src/core/cpl/core/ctx/user_context.py diff --git a/src/cpl-core/cpl/core/environment/__init__.py b/src/core/cpl/core/environment/__init__.py similarity index 100% rename from src/cpl-core/cpl/core/environment/__init__.py rename to src/core/cpl/core/environment/__init__.py diff --git a/src/cpl-core/cpl/core/environment/environment.py b/src/core/cpl/core/environment/environment.py similarity index 100% rename from src/cpl-core/cpl/core/environment/environment.py rename to src/core/cpl/core/environment/environment.py diff --git a/src/cpl-core/cpl/core/environment/environment_enum.py b/src/core/cpl/core/environment/environment_enum.py similarity index 100% rename from src/cpl-core/cpl/core/environment/environment_enum.py rename to src/core/cpl/core/environment/environment_enum.py diff --git a/src/cpl-core/cpl/core/errors.py b/src/core/cpl/core/errors.py similarity index 100% rename from src/cpl-core/cpl/core/errors.py rename to src/core/cpl/core/errors.py diff --git a/src/cpl-core/cpl/core/log/__init__.py b/src/core/cpl/core/log/__init__.py similarity index 100% rename from src/cpl-core/cpl/core/log/__init__.py rename to src/core/cpl/core/log/__init__.py diff --git a/src/cpl-core/cpl/core/log/log_level.py b/src/core/cpl/core/log/log_level.py similarity index 100% rename from src/cpl-core/cpl/core/log/log_level.py rename to src/core/cpl/core/log/log_level.py diff --git a/src/cpl-core/cpl/core/log/log_settings.py b/src/core/cpl/core/log/log_settings.py similarity index 100% rename from src/cpl-core/cpl/core/log/log_settings.py rename to src/core/cpl/core/log/log_settings.py diff --git a/src/cpl-core/cpl/core/log/logger.py b/src/core/cpl/core/log/logger.py similarity index 100% rename from src/cpl-core/cpl/core/log/logger.py rename to src/core/cpl/core/log/logger.py diff --git a/src/cpl-core/cpl/core/log/logger_abc.py b/src/core/cpl/core/log/logger_abc.py similarity index 100% rename from src/cpl-core/cpl/core/log/logger_abc.py rename to src/core/cpl/core/log/logger_abc.py diff --git a/src/cpl-core/cpl/core/log/structured_logger.py b/src/core/cpl/core/log/structured_logger.py similarity index 100% rename from src/cpl-core/cpl/core/log/structured_logger.py rename to src/core/cpl/core/log/structured_logger.py diff --git a/src/cpl-core/cpl/core/log/wrapped_logger.py b/src/core/cpl/core/log/wrapped_logger.py similarity index 100% rename from src/cpl-core/cpl/core/log/wrapped_logger.py rename to src/core/cpl/core/log/wrapped_logger.py diff --git a/src/cpl-core/cpl/core/pipes/__init__.py b/src/core/cpl/core/pipes/__init__.py similarity index 100% rename from src/cpl-core/cpl/core/pipes/__init__.py rename to src/core/cpl/core/pipes/__init__.py diff --git a/src/cpl-core/cpl/core/pipes/bool_pipe.py b/src/core/cpl/core/pipes/bool_pipe.py similarity index 100% rename from src/cpl-core/cpl/core/pipes/bool_pipe.py rename to src/core/cpl/core/pipes/bool_pipe.py diff --git a/src/cpl-core/cpl/core/pipes/ip_address_pipe.py b/src/core/cpl/core/pipes/ip_address_pipe.py similarity index 100% rename from src/cpl-core/cpl/core/pipes/ip_address_pipe.py rename to src/core/cpl/core/pipes/ip_address_pipe.py diff --git a/src/cpl-core/cpl/core/pipes/pipe_abc.py b/src/core/cpl/core/pipes/pipe_abc.py similarity index 100% rename from src/cpl-core/cpl/core/pipes/pipe_abc.py rename to src/core/cpl/core/pipes/pipe_abc.py diff --git a/src/core/cpl/core/property.py b/src/core/cpl/core/property.py new file mode 100644 index 00000000..a5b78634 --- /dev/null +++ b/src/core/cpl/core/property.py @@ -0,0 +1,3 @@ +class classproperty(property): + def __get__(self, obj, cls): + return self.fget(cls) diff --git a/src/cpl-dependency/cpl/dependency/hosted/__init__.py b/src/core/cpl/core/service/__init__.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/hosted/__init__.py rename to src/core/cpl/core/service/__init__.py diff --git a/src/cpl-dependency/cpl/dependency/hosted/cronjob.py b/src/core/cpl/core/service/cronjob.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/hosted/cronjob.py rename to src/core/cpl/core/service/cronjob.py diff --git a/src/cpl-dependency/cpl/dependency/hosted/hosted_service.py b/src/core/cpl/core/service/hosted_service.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/hosted/hosted_service.py rename to src/core/cpl/core/service/hosted_service.py diff --git a/src/cpl-dependency/cpl/dependency/hosted/startup_task.py b/src/core/cpl/core/service/startup_task.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/hosted/startup_task.py rename to src/core/cpl/core/service/startup_task.py diff --git a/src/cpl-core/cpl/core/time/__init__.py b/src/core/cpl/core/time/__init__.py similarity index 100% rename from src/cpl-core/cpl/core/time/__init__.py rename to src/core/cpl/core/time/__init__.py diff --git a/src/cpl-core/cpl/core/time/cron.py b/src/core/cpl/core/time/cron.py similarity index 100% rename from src/cpl-core/cpl/core/time/cron.py rename to src/core/cpl/core/time/cron.py diff --git a/src/cpl-core/cpl/core/time/time_format_settings.py b/src/core/cpl/core/time/time_format_settings.py similarity index 100% rename from src/cpl-core/cpl/core/time/time_format_settings.py rename to src/core/cpl/core/time/time_format_settings.py diff --git a/src/cpl-core/cpl/core/typing.py b/src/core/cpl/core/typing.py similarity index 100% rename from src/cpl-core/cpl/core/typing.py rename to src/core/cpl/core/typing.py diff --git a/src/cpl-core/cpl/core/utils/__init__.py b/src/core/cpl/core/utils/__init__.py similarity index 89% rename from src/cpl-core/cpl/core/utils/__init__.py rename to src/core/cpl/core/utils/__init__.py index c5a89180..6cad83e0 100644 --- a/src/cpl-core/cpl/core/utils/__init__.py +++ b/src/core/cpl/core/utils/__init__.py @@ -1,6 +1,5 @@ from .base64 import Base64 from .credential_manager import CredentialManager from .json_processor import JSONProcessor -from .pip import Pip from .string import String from .get_value import get_value diff --git a/src/cpl-core/cpl/core/utils/base64.py b/src/core/cpl/core/utils/base64.py similarity index 100% rename from src/cpl-core/cpl/core/utils/base64.py rename to src/core/cpl/core/utils/base64.py diff --git a/src/cpl-core/cpl/core/utils/benchmark.py b/src/core/cpl/core/utils/benchmark.py similarity index 100% rename from src/cpl-core/cpl/core/utils/benchmark.py rename to src/core/cpl/core/utils/benchmark.py diff --git a/src/cpl-core/cpl/core/utils/cache.py b/src/core/cpl/core/utils/cache.py similarity index 100% rename from src/cpl-core/cpl/core/utils/cache.py rename to src/core/cpl/core/utils/cache.py diff --git a/src/cpl-core/cpl/core/utils/cast.py b/src/core/cpl/core/utils/cast.py similarity index 100% rename from src/cpl-core/cpl/core/utils/cast.py rename to src/core/cpl/core/utils/cast.py diff --git a/src/cpl-core/cpl/core/utils/credential_manager.py b/src/core/cpl/core/utils/credential_manager.py similarity index 100% rename from src/cpl-core/cpl/core/utils/credential_manager.py rename to src/core/cpl/core/utils/credential_manager.py diff --git a/src/cpl-core/cpl/core/utils/get_value.py b/src/core/cpl/core/utils/get_value.py similarity index 100% rename from src/cpl-core/cpl/core/utils/get_value.py rename to src/core/cpl/core/utils/get_value.py diff --git a/src/cpl-core/cpl/core/utils/json_processor.py b/src/core/cpl/core/utils/json_processor.py similarity index 100% rename from src/cpl-core/cpl/core/utils/json_processor.py rename to src/core/cpl/core/utils/json_processor.py diff --git a/src/cpl-core/cpl/core/utils/number.py b/src/core/cpl/core/utils/number.py similarity index 100% rename from src/cpl-core/cpl/core/utils/number.py rename to src/core/cpl/core/utils/number.py diff --git a/src/cpl-core/cpl/core/utils/string.py b/src/core/cpl/core/utils/string.py similarity index 100% rename from src/cpl-core/cpl/core/utils/string.py rename to src/core/cpl/core/utils/string.py diff --git a/src/cpl-core/pyproject.toml b/src/core/pyproject.toml similarity index 100% rename from src/cpl-core/pyproject.toml rename to src/core/pyproject.toml diff --git a/src/cpl-database/requirements.dev.txt b/src/core/requirements.dev.txt similarity index 100% rename from src/cpl-database/requirements.dev.txt rename to src/core/requirements.dev.txt diff --git a/src/cpl-core/requirements.txt b/src/core/requirements.txt similarity index 100% rename from src/cpl-core/requirements.txt rename to src/core/requirements.txt diff --git a/src/cpl-core/cpl/core/utils/pip.py b/src/cpl-core/cpl/core/utils/pip.py deleted file mode 100644 index bc626e16..00000000 --- a/src/cpl-core/cpl/core/utils/pip.py +++ /dev/null @@ -1,130 +0,0 @@ -import os -import subprocess -import sys -from contextlib import suppress -from typing import Optional - - -class Pip: - r"""Executes pip commands""" - - _executable = sys.executable - _env = os.environ - - """Getter""" - - @classmethod - def get_executable(cls) -> str: - return cls._executable - - """Setter""" - - @classmethod - def set_executable(cls, executable: str): - r"""Sets the executable - - Parameter: - executable: :class:`str` - The python command - """ - if executable is None or executable == sys.executable: - return - - cls._executable = executable - if not os.path.islink(cls._executable) or not os.path.isfile(executable): - return - - path = os.path.dirname(os.path.dirname(cls._executable)) - cls._env = os.environ - if sys.platform == "win32": - cls._env["PATH"] = f"{path}\\bin" + os.pathsep + os.environ.get("PATH", "") - else: - cls._env["PATH"] = f"{path}/bin" + os.pathsep + os.environ.get("PATH", "") - cls._env["VIRTUAL_ENV"] = path - - @classmethod - def reset_executable(cls): - r"""Resets the executable to system standard""" - cls._executable = sys.executable - - """Public utils functions""" - - @classmethod - def get_package(cls, package: str) -> Optional[str]: - r"""Gets given package py local pip list - - Parameter: - package: :class:`str` - - Returns: - The package name as string - """ - result = None - with suppress(Exception): - args = [cls._executable, "-m", "pip", "freeze", "--all"] - - result = subprocess.check_output(args, stderr=subprocess.DEVNULL, env=cls._env) - - if result is None: - return None - for p in str(result.decode()).split("\n"): - if p.startswith(package): - return p - - return None - - @classmethod - def get_outdated(cls) -> bytes: - r"""Gets table of outdated packages - - Returns: - Bytes string of the command result - """ - args = [cls._executable, "-m", "pip", "list", "--outdated"] - - return subprocess.check_output(args, env=cls._env) - - @classmethod - def install(cls, package: str, *args, source: str = None, stdout=None, stderr=None): - r"""Installs given package - - Parameter: - package: :class:`str` - The name of the package - args: :class:`list` - Arguments for the command - source: :class:`str` - Extra index URL - stdout: :class:`str` - Stdout of subprocess.run - stderr: :class:`str` - Stderr of subprocess.run - """ - pip_args = [cls._executable, "-m", "pip", "install"] - - for arg in args: - pip_args.append(arg) - - pip_args.append(package) - - if source is not None: - pip_args.append(f"--extra-index-url") - pip_args.append(source) - - subprocess.run(pip_args, stdout=stdout, stderr=stderr, env=cls._env) - - @classmethod - def uninstall(cls, package: str, stdout=None, stderr=None): - r"""Uninstalls given package - - Parameter: - package: :class:`str` - The name of the package - stdout: :class:`str` - Stdout of subprocess.run - stderr: :class:`str` - Stderr of subprocess.run - """ - args = [cls._executable, "-m", "pip", "uninstall", "--yes", package] - - subprocess.run(args, stdout=stdout, stderr=stderr, env=cls._env) diff --git a/src/cpl-graphql/cpl/graphql/application/__init__.py b/src/cpl-graphql/cpl/graphql/application/__init__.py deleted file mode 100644 index 96b2346c..00000000 --- a/src/cpl-graphql/cpl/graphql/application/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .graphql_app import WebApp diff --git a/src/cpl-database/cpl/database/__init__.py b/src/database/cpl/database/__init__.py similarity index 88% rename from src/cpl-database/cpl/database/__init__.py rename to src/database/cpl/database/__init__.py index b9576582..c86740a8 100644 --- a/src/cpl-database/cpl/database/__init__.py +++ b/src/database/cpl/database/__init__.py @@ -3,3 +3,5 @@ from . import postgres as _postgres from .database_module import DatabaseModule from .logger import DBLogger from .table_manager import TableManager + +__version__ = "1.0.0" diff --git a/src/cpl-database/cpl/database/abc/__init__.py b/src/database/cpl/database/abc/__init__.py similarity index 100% rename from src/cpl-database/cpl/database/abc/__init__.py rename to src/database/cpl/database/abc/__init__.py diff --git a/src/cpl-database/cpl/database/abc/connection_abc.py b/src/database/cpl/database/abc/connection_abc.py similarity index 100% rename from src/cpl-database/cpl/database/abc/connection_abc.py rename to src/database/cpl/database/abc/connection_abc.py diff --git a/src/cpl-database/cpl/database/abc/data_access_object_abc.py b/src/database/cpl/database/abc/data_access_object_abc.py similarity index 100% rename from src/cpl-database/cpl/database/abc/data_access_object_abc.py rename to src/database/cpl/database/abc/data_access_object_abc.py diff --git a/src/cpl-database/cpl/database/abc/data_seeder_abc.py b/src/database/cpl/database/abc/data_seeder_abc.py similarity index 100% rename from src/cpl-database/cpl/database/abc/data_seeder_abc.py rename to src/database/cpl/database/abc/data_seeder_abc.py diff --git a/src/cpl-database/cpl/database/abc/db_context_abc.py b/src/database/cpl/database/abc/db_context_abc.py similarity index 100% rename from src/cpl-database/cpl/database/abc/db_context_abc.py rename to src/database/cpl/database/abc/db_context_abc.py diff --git a/src/cpl-database/cpl/database/abc/db_join_model_abc.py b/src/database/cpl/database/abc/db_join_model_abc.py similarity index 100% rename from src/cpl-database/cpl/database/abc/db_join_model_abc.py rename to src/database/cpl/database/abc/db_join_model_abc.py diff --git a/src/cpl-database/cpl/database/abc/db_model_abc.py b/src/database/cpl/database/abc/db_model_abc.py similarity index 100% rename from src/cpl-database/cpl/database/abc/db_model_abc.py rename to src/database/cpl/database/abc/db_model_abc.py diff --git a/src/cpl-database/cpl/database/abc/db_model_dao_abc.py b/src/database/cpl/database/abc/db_model_dao_abc.py similarity index 100% rename from src/cpl-database/cpl/database/abc/db_model_dao_abc.py rename to src/database/cpl/database/abc/db_model_dao_abc.py diff --git a/src/cpl-database/cpl/database/const.py b/src/database/cpl/database/const.py similarity index 100% rename from src/cpl-database/cpl/database/const.py rename to src/database/cpl/database/const.py diff --git a/src/cpl-database/cpl/database/database_module.py b/src/database/cpl/database/database_module.py similarity index 100% rename from src/cpl-database/cpl/database/database_module.py rename to src/database/cpl/database/database_module.py diff --git a/src/cpl-database/cpl/database/external_data_temp_table_builder.py b/src/database/cpl/database/external_data_temp_table_builder.py similarity index 100% rename from src/cpl-database/cpl/database/external_data_temp_table_builder.py rename to src/database/cpl/database/external_data_temp_table_builder.py diff --git a/src/cpl-database/cpl/database/logger.py b/src/database/cpl/database/logger.py similarity index 100% rename from src/cpl-database/cpl/database/logger.py rename to src/database/cpl/database/logger.py diff --git a/src/cpl-database/cpl/database/model/__init__.py b/src/database/cpl/database/model/__init__.py similarity index 100% rename from src/cpl-database/cpl/database/model/__init__.py rename to src/database/cpl/database/model/__init__.py diff --git a/src/cpl-database/cpl/database/model/database_settings.py b/src/database/cpl/database/model/database_settings.py similarity index 100% rename from src/cpl-database/cpl/database/model/database_settings.py rename to src/database/cpl/database/model/database_settings.py diff --git a/src/cpl-database/cpl/database/model/migration.py b/src/database/cpl/database/model/migration.py similarity index 100% rename from src/cpl-database/cpl/database/model/migration.py rename to src/database/cpl/database/model/migration.py diff --git a/src/cpl-database/cpl/database/model/server_type.py b/src/database/cpl/database/model/server_type.py similarity index 100% rename from src/cpl-database/cpl/database/model/server_type.py rename to src/database/cpl/database/model/server_type.py diff --git a/src/cpl-database/cpl/database/mysql/__init__.py b/src/database/cpl/database/mysql/__init__.py similarity index 100% rename from src/cpl-database/cpl/database/mysql/__init__.py rename to src/database/cpl/database/mysql/__init__.py diff --git a/src/cpl-database/cpl/database/mysql/connection.py b/src/database/cpl/database/mysql/connection.py similarity index 100% rename from src/cpl-database/cpl/database/mysql/connection.py rename to src/database/cpl/database/mysql/connection.py diff --git a/src/cpl-database/cpl/database/mysql/db_context.py b/src/database/cpl/database/mysql/db_context.py similarity index 100% rename from src/cpl-database/cpl/database/mysql/db_context.py rename to src/database/cpl/database/mysql/db_context.py diff --git a/src/cpl-database/cpl/database/mysql/mysql_module.py b/src/database/cpl/database/mysql/mysql_module.py similarity index 100% rename from src/cpl-database/cpl/database/mysql/mysql_module.py rename to src/database/cpl/database/mysql/mysql_module.py diff --git a/src/cpl-database/cpl/database/mysql/mysql_pool.py b/src/database/cpl/database/mysql/mysql_pool.py similarity index 100% rename from src/cpl-database/cpl/database/mysql/mysql_pool.py rename to src/database/cpl/database/mysql/mysql_pool.py diff --git a/src/cpl-database/cpl/database/postgres/__init__.py b/src/database/cpl/database/postgres/__init__.py similarity index 100% rename from src/cpl-database/cpl/database/postgres/__init__.py rename to src/database/cpl/database/postgres/__init__.py diff --git a/src/cpl-database/cpl/database/postgres/db_context.py b/src/database/cpl/database/postgres/db_context.py similarity index 100% rename from src/cpl-database/cpl/database/postgres/db_context.py rename to src/database/cpl/database/postgres/db_context.py diff --git a/src/cpl-database/cpl/database/postgres/postgres_module.py b/src/database/cpl/database/postgres/postgres_module.py similarity index 100% rename from src/cpl-database/cpl/database/postgres/postgres_module.py rename to src/database/cpl/database/postgres/postgres_module.py diff --git a/src/cpl-database/cpl/database/postgres/postgres_pool.py b/src/database/cpl/database/postgres/postgres_pool.py similarity index 100% rename from src/cpl-database/cpl/database/postgres/postgres_pool.py rename to src/database/cpl/database/postgres/postgres_pool.py diff --git a/src/cpl-database/cpl/database/postgres/sql_select_builder.py b/src/database/cpl/database/postgres/sql_select_builder.py similarity index 100% rename from src/cpl-database/cpl/database/postgres/sql_select_builder.py rename to src/database/cpl/database/postgres/sql_select_builder.py diff --git a/src/cpl-database/cpl/database/schema/__init__.py b/src/database/cpl/database/schema/__init__.py similarity index 100% rename from src/cpl-database/cpl/database/schema/__init__.py rename to src/database/cpl/database/schema/__init__.py diff --git a/src/cpl-database/cpl/database/schema/executed_migration.py b/src/database/cpl/database/schema/executed_migration.py similarity index 100% rename from src/cpl-database/cpl/database/schema/executed_migration.py rename to src/database/cpl/database/schema/executed_migration.py diff --git a/src/cpl-database/cpl/database/schema/executed_migration_dao.py b/src/database/cpl/database/schema/executed_migration_dao.py similarity index 100% rename from src/cpl-database/cpl/database/schema/executed_migration_dao.py rename to src/database/cpl/database/schema/executed_migration_dao.py diff --git a/src/cpl-database/cpl/database/scripts/mysql/0-cpl-initial.sql b/src/database/cpl/database/scripts/mysql/0-cpl-initial.sql similarity index 100% rename from src/cpl-database/cpl/database/scripts/mysql/0-cpl-initial.sql rename to src/database/cpl/database/scripts/mysql/0-cpl-initial.sql diff --git a/src/cpl-database/cpl/database/scripts/mysql/trigger.txt b/src/database/cpl/database/scripts/mysql/trigger.txt similarity index 100% rename from src/cpl-database/cpl/database/scripts/mysql/trigger.txt rename to src/database/cpl/database/scripts/mysql/trigger.txt diff --git a/src/cpl-database/cpl/database/scripts/postgres/0-cpl-initial.sql b/src/database/cpl/database/scripts/postgres/0-cpl-initial.sql similarity index 100% rename from src/cpl-database/cpl/database/scripts/postgres/0-cpl-initial.sql rename to src/database/cpl/database/scripts/postgres/0-cpl-initial.sql diff --git a/src/cpl-database/cpl/database/service/__init__.py b/src/database/cpl/database/service/__init__.py similarity index 100% rename from src/cpl-database/cpl/database/service/__init__.py rename to src/database/cpl/database/service/__init__.py diff --git a/src/cpl-database/cpl/database/service/migration_service.py b/src/database/cpl/database/service/migration_service.py similarity index 100% rename from src/cpl-database/cpl/database/service/migration_service.py rename to src/database/cpl/database/service/migration_service.py diff --git a/src/cpl-database/cpl/database/service/seeder_service.py b/src/database/cpl/database/service/seeder_service.py similarity index 100% rename from src/cpl-database/cpl/database/service/seeder_service.py rename to src/database/cpl/database/service/seeder_service.py diff --git a/src/cpl-database/cpl/database/table_manager.py b/src/database/cpl/database/table_manager.py similarity index 100% rename from src/cpl-database/cpl/database/table_manager.py rename to src/database/cpl/database/table_manager.py diff --git a/src/cpl-database/cpl/database/typing.py b/src/database/cpl/database/typing.py similarity index 100% rename from src/cpl-database/cpl/database/typing.py rename to src/database/cpl/database/typing.py diff --git a/src/cpl-database/pyproject.toml b/src/database/pyproject.toml similarity index 100% rename from src/cpl-database/pyproject.toml rename to src/database/pyproject.toml diff --git a/src/cpl-dependency/requirements.dev.txt b/src/database/requirements.dev.txt similarity index 100% rename from src/cpl-dependency/requirements.dev.txt rename to src/database/requirements.dev.txt diff --git a/src/cpl-database/requirements.txt b/src/database/requirements.txt similarity index 100% rename from src/cpl-database/requirements.txt rename to src/database/requirements.txt diff --git a/src/cpl-dependency/cpl/dependency/__init__.py b/src/dependency/cpl/dependency/__init__.py similarity index 93% rename from src/cpl-dependency/cpl/dependency/__init__.py rename to src/dependency/cpl/dependency/__init__.py index fa94d32c..17394656 100644 --- a/src/cpl-dependency/cpl/dependency/__init__.py +++ b/src/dependency/cpl/dependency/__init__.py @@ -5,3 +5,5 @@ from .service_descriptor import ServiceDescriptor from .service_lifetime import ServiceLifetimeEnum from .service_provider import ServiceProvider from .service_provider import ServiceProvider + +__version__ = "1.0.0" diff --git a/src/cpl-dependency/cpl/dependency/context.py b/src/dependency/cpl/dependency/context.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/context.py rename to src/dependency/cpl/dependency/context.py diff --git a/src/cpl-dependency/cpl/dependency/event_bus.py b/src/dependency/cpl/dependency/event_bus.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/event_bus.py rename to src/dependency/cpl/dependency/event_bus.py diff --git a/src/cpl-dependency/cpl/dependency/inject.py b/src/dependency/cpl/dependency/inject.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/inject.py rename to src/dependency/cpl/dependency/inject.py diff --git a/src/dependency/cpl/dependency/module/__init__.py b/src/dependency/cpl/dependency/module/__init__.py new file mode 100644 index 00000000..8f210ac9 --- /dev/null +++ b/src/dependency/cpl/dependency/module/__init__.py @@ -0,0 +1 @@ +from .module import Module diff --git a/src/cpl-dependency/cpl/dependency/module/module.py b/src/dependency/cpl/dependency/module/module.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/module/module.py rename to src/dependency/cpl/dependency/module/module.py diff --git a/src/cpl-dependency/cpl/dependency/module/module_abc.py b/src/dependency/cpl/dependency/module/module_abc.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/module/module_abc.py rename to src/dependency/cpl/dependency/module/module_abc.py diff --git a/src/cpl-dependency/cpl/dependency/module/module_protocol.py b/src/dependency/cpl/dependency/module/module_protocol.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/module/module_protocol.py rename to src/dependency/cpl/dependency/module/module_protocol.py diff --git a/src/cpl-dependency/cpl/dependency/service_collection.py b/src/dependency/cpl/dependency/service_collection.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/service_collection.py rename to src/dependency/cpl/dependency/service_collection.py diff --git a/src/cpl-dependency/cpl/dependency/service_descriptor.py b/src/dependency/cpl/dependency/service_descriptor.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/service_descriptor.py rename to src/dependency/cpl/dependency/service_descriptor.py diff --git a/src/cpl-dependency/cpl/dependency/service_lifetime.py b/src/dependency/cpl/dependency/service_lifetime.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/service_lifetime.py rename to src/dependency/cpl/dependency/service_lifetime.py diff --git a/src/cpl-dependency/cpl/dependency/service_provider.py b/src/dependency/cpl/dependency/service_provider.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/service_provider.py rename to src/dependency/cpl/dependency/service_provider.py diff --git a/src/cpl-dependency/cpl/dependency/typing.py b/src/dependency/cpl/dependency/typing.py similarity index 100% rename from src/cpl-dependency/cpl/dependency/typing.py rename to src/dependency/cpl/dependency/typing.py diff --git a/src/cpl-dependency/pyproject.toml b/src/dependency/pyproject.toml similarity index 100% rename from src/cpl-dependency/pyproject.toml rename to src/dependency/pyproject.toml diff --git a/src/cpl-graphql/requirements.dev.txt b/src/dependency/requirements.dev.txt similarity index 100% rename from src/cpl-graphql/requirements.dev.txt rename to src/dependency/requirements.dev.txt diff --git a/src/cpl-dependency/requirements.txt b/src/dependency/requirements.txt similarity index 100% rename from src/cpl-dependency/requirements.txt rename to src/dependency/requirements.txt diff --git a/src/graphql/cpl/graphql/__init__.py b/src/graphql/cpl/graphql/__init__.py new file mode 100644 index 00000000..5becc17c --- /dev/null +++ b/src/graphql/cpl/graphql/__init__.py @@ -0,0 +1 @@ +__version__ = "1.0.0" diff --git a/src/cpl-graphql/cpl/graphql/auth/role/__init__.py b/src/graphql/cpl/graphql/_endpoints/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/role/__init__.py rename to src/graphql/cpl/graphql/_endpoints/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/_endpoints/graphiql.py b/src/graphql/cpl/graphql/_endpoints/graphiql.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/_endpoints/graphiql.py rename to src/graphql/cpl/graphql/_endpoints/graphiql.py diff --git a/src/cpl-graphql/cpl/graphql/_endpoints/graphql.py b/src/graphql/cpl/graphql/_endpoints/graphql.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/_endpoints/graphql.py rename to src/graphql/cpl/graphql/_endpoints/graphql.py diff --git a/src/cpl-graphql/cpl/graphql/_endpoints/lazy_graphql_app.py b/src/graphql/cpl/graphql/_endpoints/lazy_graphql_app.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/_endpoints/lazy_graphql_app.py rename to src/graphql/cpl/graphql/_endpoints/lazy_graphql_app.py diff --git a/src/cpl-graphql/cpl/graphql/_endpoints/playground.py b/src/graphql/cpl/graphql/_endpoints/playground.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/_endpoints/playground.py rename to src/graphql/cpl/graphql/_endpoints/playground.py diff --git a/src/cpl-graphql/cpl/graphql/auth/user/__init__.py b/src/graphql/cpl/graphql/abc/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/user/__init__.py rename to src/graphql/cpl/graphql/abc/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/abc/query_abc.py b/src/graphql/cpl/graphql/abc/query_abc.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/abc/query_abc.py rename to src/graphql/cpl/graphql/abc/query_abc.py diff --git a/src/cpl-graphql/cpl/graphql/abc/strawberry_protocol.py b/src/graphql/cpl/graphql/abc/strawberry_protocol.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/abc/strawberry_protocol.py rename to src/graphql/cpl/graphql/abc/strawberry_protocol.py diff --git a/src/graphql/cpl/graphql/application/__init__.py b/src/graphql/cpl/graphql/application/__init__.py new file mode 100644 index 00000000..cd74b311 --- /dev/null +++ b/src/graphql/cpl/graphql/application/__init__.py @@ -0,0 +1 @@ +from .graphql_app import GraphQLApp diff --git a/src/cpl-graphql/cpl/graphql/application/graphql_app.py b/src/graphql/cpl/graphql/application/graphql_app.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/application/graphql_app.py rename to src/graphql/cpl/graphql/application/graphql_app.py diff --git a/src/cpl-graphql/cpl/graphql/event_bus/__init__.py b/src/graphql/cpl/graphql/auth/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/event_bus/__init__.py rename to src/graphql/cpl/graphql/auth/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/schema/__init__.py b/src/graphql/cpl/graphql/auth/api_key/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/__init__.py rename to src/graphql/cpl/graphql/auth/api_key/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/auth/api_key/api_key_filter.py b/src/graphql/cpl/graphql/auth/api_key/api_key_filter.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/api_key/api_key_filter.py rename to src/graphql/cpl/graphql/auth/api_key/api_key_filter.py diff --git a/src/cpl-graphql/cpl/graphql/auth/api_key/api_key_graph_type.py b/src/graphql/cpl/graphql/auth/api_key/api_key_graph_type.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/api_key/api_key_graph_type.py rename to src/graphql/cpl/graphql/auth/api_key/api_key_graph_type.py diff --git a/src/cpl-graphql/cpl/graphql/auth/api_key/api_key_input.py b/src/graphql/cpl/graphql/auth/api_key/api_key_input.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/api_key/api_key_input.py rename to src/graphql/cpl/graphql/auth/api_key/api_key_input.py diff --git a/src/cpl-graphql/cpl/graphql/auth/api_key/api_key_mutation.py b/src/graphql/cpl/graphql/auth/api_key/api_key_mutation.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/api_key/api_key_mutation.py rename to src/graphql/cpl/graphql/auth/api_key/api_key_mutation.py diff --git a/src/cpl-graphql/cpl/graphql/auth/api_key/api_key_sort.py b/src/graphql/cpl/graphql/auth/api_key/api_key_sort.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/api_key/api_key_sort.py rename to src/graphql/cpl/graphql/auth/api_key/api_key_sort.py diff --git a/src/cpl-graphql/cpl/graphql/auth/graphql_auth_module.py b/src/graphql/cpl/graphql/auth/graphql_auth_module.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/graphql_auth_module.py rename to src/graphql/cpl/graphql/auth/graphql_auth_module.py diff --git a/src/cpl-graphql/cpl/graphql/schema/filter/__init__.py b/src/graphql/cpl/graphql/auth/role/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/filter/__init__.py rename to src/graphql/cpl/graphql/auth/role/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/auth/role/role_filter.py b/src/graphql/cpl/graphql/auth/role/role_filter.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/role/role_filter.py rename to src/graphql/cpl/graphql/auth/role/role_filter.py diff --git a/src/cpl-graphql/cpl/graphql/auth/role/role_graph_type.py b/src/graphql/cpl/graphql/auth/role/role_graph_type.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/role/role_graph_type.py rename to src/graphql/cpl/graphql/auth/role/role_graph_type.py diff --git a/src/cpl-graphql/cpl/graphql/auth/role/role_input.py b/src/graphql/cpl/graphql/auth/role/role_input.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/role/role_input.py rename to src/graphql/cpl/graphql/auth/role/role_input.py diff --git a/src/cpl-graphql/cpl/graphql/auth/role/role_mutation.py b/src/graphql/cpl/graphql/auth/role/role_mutation.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/role/role_mutation.py rename to src/graphql/cpl/graphql/auth/role/role_mutation.py diff --git a/src/cpl-graphql/cpl/graphql/auth/role/role_sort.py b/src/graphql/cpl/graphql/auth/role/role_sort.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/role/role_sort.py rename to src/graphql/cpl/graphql/auth/role/role_sort.py diff --git a/src/cpl-graphql/cpl/graphql/schema/sort/__init__.py b/src/graphql/cpl/graphql/auth/user/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/sort/__init__.py rename to src/graphql/cpl/graphql/auth/user/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/auth/user/user_filter.py b/src/graphql/cpl/graphql/auth/user/user_filter.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/user/user_filter.py rename to src/graphql/cpl/graphql/auth/user/user_filter.py diff --git a/src/cpl-graphql/cpl/graphql/auth/user/user_graph_type.py b/src/graphql/cpl/graphql/auth/user/user_graph_type.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/user/user_graph_type.py rename to src/graphql/cpl/graphql/auth/user/user_graph_type.py diff --git a/src/cpl-graphql/cpl/graphql/auth/user/user_input.py b/src/graphql/cpl/graphql/auth/user/user_input.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/user/user_input.py rename to src/graphql/cpl/graphql/auth/user/user_input.py diff --git a/src/cpl-graphql/cpl/graphql/auth/user/user_mutation.py b/src/graphql/cpl/graphql/auth/user/user_mutation.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/user/user_mutation.py rename to src/graphql/cpl/graphql/auth/user/user_mutation.py diff --git a/src/cpl-graphql/cpl/graphql/auth/user/user_sort.py b/src/graphql/cpl/graphql/auth/user/user_sort.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/auth/user/user_sort.py rename to src/graphql/cpl/graphql/auth/user/user_sort.py diff --git a/src/cpl-graphql/cpl/graphql/error.py b/src/graphql/cpl/graphql/error.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/error.py rename to src/graphql/cpl/graphql/error.py diff --git a/src/cpl-graphql/cpl/graphql/service/__init__.py b/src/graphql/cpl/graphql/event_bus/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/service/__init__.py rename to src/graphql/cpl/graphql/event_bus/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/event_bus/memory.py b/src/graphql/cpl/graphql/event_bus/memory.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/event_bus/memory.py rename to src/graphql/cpl/graphql/event_bus/memory.py diff --git a/src/cpl-graphql/cpl/graphql/graphql_module.py b/src/graphql/cpl/graphql/graphql_module.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/graphql_module.py rename to src/graphql/cpl/graphql/graphql_module.py diff --git a/src/cpl-graphql/cpl/graphql/query_context.py b/src/graphql/cpl/graphql/query_context.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/query_context.py rename to src/graphql/cpl/graphql/query_context.py diff --git a/src/cpl-graphql/cpl/graphql/utils/__init__.py b/src/graphql/cpl/graphql/schema/__init__.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/utils/__init__.py rename to src/graphql/cpl/graphql/schema/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/schema/argument.py b/src/graphql/cpl/graphql/schema/argument.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/argument.py rename to src/graphql/cpl/graphql/schema/argument.py diff --git a/src/cpl-graphql/cpl/graphql/schema/collection.py b/src/graphql/cpl/graphql/schema/collection.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/collection.py rename to src/graphql/cpl/graphql/schema/collection.py diff --git a/src/cpl-graphql/cpl/graphql/schema/db_model_graph_type.py b/src/graphql/cpl/graphql/schema/db_model_graph_type.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/db_model_graph_type.py rename to src/graphql/cpl/graphql/schema/db_model_graph_type.py diff --git a/src/cpl-graphql/cpl/graphql/schema/field.py b/src/graphql/cpl/graphql/schema/field.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/field.py rename to src/graphql/cpl/graphql/schema/field.py diff --git a/src/cpl-mail/cpl/mail/abc/__init__.py b/src/graphql/cpl/graphql/schema/filter/__init__.py similarity index 100% rename from src/cpl-mail/cpl/mail/abc/__init__.py rename to src/graphql/cpl/graphql/schema/filter/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/schema/filter/bool_filter.py b/src/graphql/cpl/graphql/schema/filter/bool_filter.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/filter/bool_filter.py rename to src/graphql/cpl/graphql/schema/filter/bool_filter.py diff --git a/src/cpl-graphql/cpl/graphql/schema/filter/date_filter.py b/src/graphql/cpl/graphql/schema/filter/date_filter.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/filter/date_filter.py rename to src/graphql/cpl/graphql/schema/filter/date_filter.py diff --git a/src/cpl-graphql/cpl/graphql/schema/filter/db_model_filter.py b/src/graphql/cpl/graphql/schema/filter/db_model_filter.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/filter/db_model_filter.py rename to src/graphql/cpl/graphql/schema/filter/db_model_filter.py diff --git a/src/cpl-graphql/cpl/graphql/schema/filter/filter.py b/src/graphql/cpl/graphql/schema/filter/filter.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/filter/filter.py rename to src/graphql/cpl/graphql/schema/filter/filter.py diff --git a/src/cpl-graphql/cpl/graphql/schema/filter/int_filter.py b/src/graphql/cpl/graphql/schema/filter/int_filter.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/filter/int_filter.py rename to src/graphql/cpl/graphql/schema/filter/int_filter.py diff --git a/src/cpl-graphql/cpl/graphql/schema/filter/string_filter.py b/src/graphql/cpl/graphql/schema/filter/string_filter.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/filter/string_filter.py rename to src/graphql/cpl/graphql/schema/filter/string_filter.py diff --git a/src/cpl-graphql/cpl/graphql/schema/graph_type.py b/src/graphql/cpl/graphql/schema/graph_type.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/graph_type.py rename to src/graphql/cpl/graphql/schema/graph_type.py diff --git a/src/cpl-graphql/cpl/graphql/schema/input.py b/src/graphql/cpl/graphql/schema/input.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/input.py rename to src/graphql/cpl/graphql/schema/input.py diff --git a/src/cpl-graphql/cpl/graphql/schema/mutation.py b/src/graphql/cpl/graphql/schema/mutation.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/mutation.py rename to src/graphql/cpl/graphql/schema/mutation.py diff --git a/src/cpl-graphql/cpl/graphql/schema/query.py b/src/graphql/cpl/graphql/schema/query.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/query.py rename to src/graphql/cpl/graphql/schema/query.py diff --git a/src/cpl-graphql/cpl/graphql/schema/root_mutation.py b/src/graphql/cpl/graphql/schema/root_mutation.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/root_mutation.py rename to src/graphql/cpl/graphql/schema/root_mutation.py diff --git a/src/cpl-graphql/cpl/graphql/schema/root_query.py b/src/graphql/cpl/graphql/schema/root_query.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/root_query.py rename to src/graphql/cpl/graphql/schema/root_query.py diff --git a/src/cpl-graphql/cpl/graphql/schema/root_subscription.py b/src/graphql/cpl/graphql/schema/root_subscription.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/root_subscription.py rename to src/graphql/cpl/graphql/schema/root_subscription.py diff --git a/src/cpl-query/requirements.txt b/src/graphql/cpl/graphql/schema/sort/__init__.py similarity index 100% rename from src/cpl-query/requirements.txt rename to src/graphql/cpl/graphql/schema/sort/__init__.py diff --git a/src/cpl-graphql/cpl/graphql/schema/sort/db_model_sort.py b/src/graphql/cpl/graphql/schema/sort/db_model_sort.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/sort/db_model_sort.py rename to src/graphql/cpl/graphql/schema/sort/db_model_sort.py diff --git a/src/cpl-graphql/cpl/graphql/schema/sort/sort.py b/src/graphql/cpl/graphql/schema/sort/sort.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/sort/sort.py rename to src/graphql/cpl/graphql/schema/sort/sort.py diff --git a/src/cpl-graphql/cpl/graphql/schema/sort/sort_order.py b/src/graphql/cpl/graphql/schema/sort/sort_order.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/sort/sort_order.py rename to src/graphql/cpl/graphql/schema/sort/sort_order.py diff --git a/src/cpl-graphql/cpl/graphql/schema/subscription.py b/src/graphql/cpl/graphql/schema/subscription.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/subscription.py rename to src/graphql/cpl/graphql/schema/subscription.py diff --git a/src/cpl-graphql/cpl/graphql/schema/subscription_field.py b/src/graphql/cpl/graphql/schema/subscription_field.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/schema/subscription_field.py rename to src/graphql/cpl/graphql/schema/subscription_field.py diff --git a/src/graphql/cpl/graphql/service/__init__.py b/src/graphql/cpl/graphql/service/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/cpl-graphql/cpl/graphql/service/graphql.py b/src/graphql/cpl/graphql/service/graphql.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/service/graphql.py rename to src/graphql/cpl/graphql/service/graphql.py diff --git a/src/cpl-graphql/cpl/graphql/service/schema.py b/src/graphql/cpl/graphql/service/schema.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/service/schema.py rename to src/graphql/cpl/graphql/service/schema.py diff --git a/src/cpl-graphql/cpl/graphql/typing.py b/src/graphql/cpl/graphql/typing.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/typing.py rename to src/graphql/cpl/graphql/typing.py diff --git a/src/graphql/cpl/graphql/utils/__init__.py b/src/graphql/cpl/graphql/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/cpl-graphql/cpl/graphql/utils/name_pipe.py b/src/graphql/cpl/graphql/utils/name_pipe.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/utils/name_pipe.py rename to src/graphql/cpl/graphql/utils/name_pipe.py diff --git a/src/cpl-graphql/cpl/graphql/utils/type_collector.py b/src/graphql/cpl/graphql/utils/type_collector.py similarity index 100% rename from src/cpl-graphql/cpl/graphql/utils/type_collector.py rename to src/graphql/cpl/graphql/utils/type_collector.py diff --git a/src/cpl-graphql/pyproject.toml b/src/graphql/pyproject.toml similarity index 100% rename from src/cpl-graphql/pyproject.toml rename to src/graphql/pyproject.toml diff --git a/src/cpl-mail/requirements.dev.txt b/src/graphql/requirements.dev.txt similarity index 100% rename from src/cpl-mail/requirements.dev.txt rename to src/graphql/requirements.dev.txt diff --git a/src/cpl-graphql/requirements.txt b/src/graphql/requirements.txt similarity index 100% rename from src/cpl-graphql/requirements.txt rename to src/graphql/requirements.txt diff --git a/src/cpl-mail/cpl/mail/__init__.py b/src/mail/cpl/mail/__init__.py similarity index 91% rename from src/cpl-mail/cpl/mail/__init__.py rename to src/mail/cpl/mail/__init__.py index 97336c8e..c0b64ed2 100644 --- a/src/cpl-mail/cpl/mail/__init__.py +++ b/src/mail/cpl/mail/__init__.py @@ -4,3 +4,5 @@ from .email_client_settings import EMailClientSettings from .email_model import EMail from .logger import MailLogger from .mail_module import MailModule + +__version__ = "1.0.0" diff --git a/src/mail/cpl/mail/abc/__init__.py b/src/mail/cpl/mail/abc/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/cpl-mail/cpl/mail/abc/email_client_abc.py b/src/mail/cpl/mail/abc/email_client_abc.py similarity index 100% rename from src/cpl-mail/cpl/mail/abc/email_client_abc.py rename to src/mail/cpl/mail/abc/email_client_abc.py diff --git a/src/cpl-mail/cpl/mail/email_client.py b/src/mail/cpl/mail/email_client.py similarity index 100% rename from src/cpl-mail/cpl/mail/email_client.py rename to src/mail/cpl/mail/email_client.py diff --git a/src/cpl-mail/cpl/mail/email_client_settings.py b/src/mail/cpl/mail/email_client_settings.py similarity index 100% rename from src/cpl-mail/cpl/mail/email_client_settings.py rename to src/mail/cpl/mail/email_client_settings.py diff --git a/src/cpl-mail/cpl/mail/email_model.py b/src/mail/cpl/mail/email_model.py similarity index 100% rename from src/cpl-mail/cpl/mail/email_model.py rename to src/mail/cpl/mail/email_model.py diff --git a/src/cpl-mail/cpl/mail/logger.py b/src/mail/cpl/mail/logger.py similarity index 100% rename from src/cpl-mail/cpl/mail/logger.py rename to src/mail/cpl/mail/logger.py diff --git a/src/cpl-mail/cpl/mail/mail_module.py b/src/mail/cpl/mail/mail_module.py similarity index 100% rename from src/cpl-mail/cpl/mail/mail_module.py rename to src/mail/cpl/mail/mail_module.py diff --git a/src/cpl-mail/pyproject.toml b/src/mail/pyproject.toml similarity index 100% rename from src/cpl-mail/pyproject.toml rename to src/mail/pyproject.toml diff --git a/src/cpl-query/requirements.dev.txt b/src/mail/requirements.dev.txt similarity index 100% rename from src/cpl-query/requirements.dev.txt rename to src/mail/requirements.dev.txt diff --git a/src/cpl-mail/requirements.txt b/src/mail/requirements.txt similarity index 100% rename from src/cpl-mail/requirements.txt rename to src/mail/requirements.txt diff --git a/src/cpl-query/cpl/query/__init__.py b/src/query/cpl/query/__init__.py similarity index 91% rename from src/cpl-query/cpl/query/__init__.py rename to src/query/cpl/query/__init__.py index 9ae9b246..933ac92f 100644 --- a/src/cpl-query/cpl/query/__init__.py +++ b/src/query/cpl/query/__init__.py @@ -5,3 +5,5 @@ from .immutable_set import ImmutableSet from .list import List from .ordered_enumerable import OrderedEnumerable from .set import Set + +__version__ = "1.0.0" diff --git a/src/cpl-query/cpl/query/array.py b/src/query/cpl/query/array.py similarity index 100% rename from src/cpl-query/cpl/query/array.py rename to src/query/cpl/query/array.py diff --git a/src/cpl-query/cpl/query/enumerable.py b/src/query/cpl/query/enumerable.py similarity index 100% rename from src/cpl-query/cpl/query/enumerable.py rename to src/query/cpl/query/enumerable.py diff --git a/src/cpl-query/cpl/query/immutable_list.py b/src/query/cpl/query/immutable_list.py similarity index 100% rename from src/cpl-query/cpl/query/immutable_list.py rename to src/query/cpl/query/immutable_list.py diff --git a/src/cpl-query/cpl/query/immutable_set.py b/src/query/cpl/query/immutable_set.py similarity index 100% rename from src/cpl-query/cpl/query/immutable_set.py rename to src/query/cpl/query/immutable_set.py diff --git a/src/cpl-query/cpl/query/list.py b/src/query/cpl/query/list.py similarity index 100% rename from src/cpl-query/cpl/query/list.py rename to src/query/cpl/query/list.py diff --git a/src/cpl-query/cpl/query/ordered_enumerable.py b/src/query/cpl/query/ordered_enumerable.py similarity index 100% rename from src/cpl-query/cpl/query/ordered_enumerable.py rename to src/query/cpl/query/ordered_enumerable.py diff --git a/src/cpl-query/cpl/query/protocol/__init__.py b/src/query/cpl/query/protocol/__init__.py similarity index 100% rename from src/cpl-query/cpl/query/protocol/__init__.py rename to src/query/cpl/query/protocol/__init__.py diff --git a/src/cpl-query/cpl/query/protocol/sequence.py b/src/query/cpl/query/protocol/sequence.py similarity index 100% rename from src/cpl-query/cpl/query/protocol/sequence.py rename to src/query/cpl/query/protocol/sequence.py diff --git a/src/cpl-query/cpl/query/set.py b/src/query/cpl/query/set.py similarity index 100% rename from src/cpl-query/cpl/query/set.py rename to src/query/cpl/query/set.py diff --git a/src/cpl-query/cpl/query/typing.py b/src/query/cpl/query/typing.py similarity index 100% rename from src/cpl-query/cpl/query/typing.py rename to src/query/cpl/query/typing.py diff --git a/src/cpl-query/pyproject.toml b/src/query/pyproject.toml similarity index 100% rename from src/cpl-query/pyproject.toml rename to src/query/pyproject.toml diff --git a/src/cpl-translation/requirements.dev.txt b/src/query/requirements.dev.txt similarity index 100% rename from src/cpl-translation/requirements.dev.txt rename to src/query/requirements.dev.txt diff --git a/src/query/requirements.txt b/src/query/requirements.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/cpl-translation/cpl/translation/__init__.py b/src/translation/cpl/translation/__init__.py similarity index 91% rename from src/cpl-translation/cpl/translation/__init__.py rename to src/translation/cpl/translation/__init__.py index 8bcc4f38..9ec4f91f 100644 --- a/src/cpl-translation/cpl/translation/__init__.py +++ b/src/translation/cpl/translation/__init__.py @@ -3,3 +3,5 @@ from .translation_module import TranslationModule from .translation_service import TranslationService from .translation_service_abc import TranslationServiceABC from .translation_settings import TranslationSettings + +__version__ = "1.0.0" diff --git a/src/cpl-translation/cpl/translation/translate_pipe.py b/src/translation/cpl/translation/translate_pipe.py similarity index 100% rename from src/cpl-translation/cpl/translation/translate_pipe.py rename to src/translation/cpl/translation/translate_pipe.py diff --git a/src/cpl-translation/cpl/translation/translation_module.py b/src/translation/cpl/translation/translation_module.py similarity index 100% rename from src/cpl-translation/cpl/translation/translation_module.py rename to src/translation/cpl/translation/translation_module.py diff --git a/src/cpl-translation/cpl/translation/translation_service.py b/src/translation/cpl/translation/translation_service.py similarity index 100% rename from src/cpl-translation/cpl/translation/translation_service.py rename to src/translation/cpl/translation/translation_service.py diff --git a/src/cpl-translation/cpl/translation/translation_service_abc.py b/src/translation/cpl/translation/translation_service_abc.py similarity index 100% rename from src/cpl-translation/cpl/translation/translation_service_abc.py rename to src/translation/cpl/translation/translation_service_abc.py diff --git a/src/cpl-translation/cpl/translation/translation_settings.py b/src/translation/cpl/translation/translation_settings.py similarity index 100% rename from src/cpl-translation/cpl/translation/translation_settings.py rename to src/translation/cpl/translation/translation_settings.py diff --git a/src/cpl-translation/pyproject.toml b/src/translation/pyproject.toml similarity index 100% rename from src/cpl-translation/pyproject.toml rename to src/translation/pyproject.toml diff --git a/src/translation/requirements.dev.txt b/src/translation/requirements.dev.txt new file mode 100644 index 00000000..e7664b42 --- /dev/null +++ b/src/translation/requirements.dev.txt @@ -0,0 +1 @@ +black==25.1.0 \ No newline at end of file diff --git a/src/cpl-translation/requirements.txt b/src/translation/requirements.txt similarity index 100% rename from src/cpl-translation/requirements.txt rename to src/translation/requirements.txt