WIP: dev into master #184

Draft
edraft wants to merge 121 commits from dev into master
289 changed files with 259 additions and 9353 deletions
Showing only changes of commit 2d2bb86720 - Show all commits

View File

@@ -0,0 +1,14 @@
name: Build on push
run-name: Build on push
on:
push:
branches:
- dev
jobs:
prepare:
uses: ./.gitea/workflows/prepare.yaml
build:
needs: prepare
uses: ./.gitea/workflows/package.yaml

View File

@@ -0,0 +1,47 @@
name: Build Package
run-name: Build Python Package
on:
workflow_call:
jobs:
build:
runs-on: [ runner ]
needs: prepare
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
steps:
- name: Clone Repository
uses: https://github.com/actions/checkout@v3
with:
token: ${{ secrets.CI_ACCESS_TOKEN }}
- name: Download build version artifact
uses: actions/download-artifact@v3
with:
name: version
- name: Set version
run: |
sed -i -E "s/^version = \".*\"/version = \"$(cat version.txt)-dev\"/" pyproject.toml
- name: Install Dependencies
run: |
pip install build
- name: Build Package
run: |
python -m build --sdist --wheel --outdir dist
upload:
- name: Login to registry git.sh-edraft.de
uses: https://github.com/docker/login-action@v1
with:
registry: git.sh-edraft.de
username: ${{ secrets.CI_USERNAME }}
password: ${{ secrets.CI_ACCESS_TOKEN }}
- name: Push image
run: |
pip install twine
python -m twine upload --repository-url https://git.sh-edraft.de/api/packages/sh-edraft.de/pypi -u ${{ secrets.CI_USERNAME }} -p ${{ secrets.CI_ACCESS_TOKEN }} ./dist/*

View File

@@ -0,0 +1,43 @@
name: Prepare Build
run-name: Prepare Build Version
on:
workflow_call:
outputs:
build_version:
value: ${{ jobs.prepare.outputs.build_version }}
jobs:
prepare:
runs-on: [ runner ]
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
steps:
- name: Clone Repository
uses: https://github.com/actions/checkout@v3
with:
token: ${{ secrets.CI_ACCESS_TOKEN }}
- name: Get Date and Build Number
run: |
git fetch --tags
git tag
DATE=$(date +'%Y.%m.%d')
TAG_COUNT=$(git tag -l "${DATE}.*" | wc -l)
BUILD_NUMBER=$(($TAG_COUNT + 1))
BUILD_VERSION="${DATE}.${BUILD_NUMBER}.dev1"
echo "$BUILD_VERSION" > version.txt
echo "VERSION $BUILD_VERSION"
- name: Create Git Tag for Build
run: |
git config user.name "ci"
git config user.email "dev@sh-edraft.de"
echo "tag $(cat version.txt)"
git tag $(cat version.txt)
git push origin --tags
- name: Upload build version artifact
uses: actions/upload-artifact@v3
with:
name: version
path: version.txt

View File

@@ -0,0 +1,22 @@
name: Publish Package
run-name: Publish Python Package
on:
workflow_call:
jobs:
upload:
runs-on: [ runner ]
container: git.sh-edraft.de/sh-edraft.de/act-runner:latest
steps:
- name: Login to registry git.sh-edraft.de
uses: https://github.com/docker/login-action@v1
with:
registry: git.sh-edraft.de
username: ${{ secrets.CI_USERNAME }}
password: ${{ secrets.CI_ACCESS_TOKEN }}
- name: Push image
run: |
pip install twine
python -m twine upload --repository-url https://git.sh-edraft.de/api/packages/sh-edraft.de/pypi -u ${{ secrets.CI_USERNAME }} -p ${{ secrets.CI_ACCESS_TOKEN }} ./dist/*

79
clean_init.py Normal file
View File

@@ -0,0 +1,79 @@
import os
import re
def clean_init_files(root_dir='.'):
"""
Durchsucht rekursiv alle __init__.py Dateien und entfernt:
- Einzeilige Kommentare
- Mehrzeilige Kommentare/Docstrings
- Variablen, die mit __ beginnen und enden
- VersionInfo- und version_info-Zuweisungen
"""
# Regex-Pattern für zu entfernende Zeilen
comment_pattern = re.compile(r'^\s*#.*$')
dunder_pattern = re.compile(r'^\s*__\w+__\s*=.*$')
version_pattern = re.compile(r'^\s*(VersionInfo|version_info)\s*=.*$')
# Zu ignorierende Verzeichnisse
ignore_dirs = ['venv', '.venv', 'env', '.env']
# Zähle bearbeitete Dateien
processed_files = 0
for dirpath, dirnames, filenames in os.walk(root_dir):
# Ignoriere venv-Verzeichnisse
dirnames[:] = [d for d in dirnames if d not in ignore_dirs]
for filename in filenames:
if filename == '__init__.py':
file_path = os.path.join(dirpath, filename)
# Datei lesen
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Mehrzeilige Kommentare entfernen
# Entferne Docstrings (''' oder """)
content = re.sub(r'(?:\'\'\'|""")[\s\S]*?(?:\'\'\'|""")', '', content)
# Verarbeite die Zeilen
lines = content.splitlines()
# Zu behaltende Zeilen filtern
filtered_lines = []
for line in lines:
if (not comment_pattern.match(line) and
not dunder_pattern.match(line) and
not version_pattern.match(line)):
filtered_lines.append(line)
# Leere aufeinanderfolgende Zeilen entfernen
clean_lines = []
prev_empty = False
for line in filtered_lines:
is_empty = line.strip() == ''
if not (is_empty and prev_empty):
clean_lines.append(line)
prev_empty = is_empty
# Entferne Leerzeilen am Anfang und Ende
while clean_lines and clean_lines[0].strip() == '':
clean_lines.pop(0)
while clean_lines and clean_lines[-1].strip() == '':
clean_lines.pop()
# Füge eine Leerzeile am Ende hinzu, wenn die Datei nicht leer ist
if clean_lines:
clean_lines.append('')
# Schreibe bereinigte Datei zurück
with open(file_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(clean_lines))
processed_files += 1
print(f"Bereinigt: {file_path}")
print(f"\nAbgeschlossen: {processed_files} __init__.py Dateien bereinigt")
if __name__ == "__main__":
clean_init_files()

View File

@@ -1,151 +0,0 @@
{
"WorkspaceSettings": {
"DefaultProject": "cpl-core",
"Projects": {
"cpl-cli": "src/cpl_cli/cpl-cli.json",
"cpl-core": "src/cpl_core/cpl-core.json",
"cpl-discord": "src/cpl_discord/cpl-discord.json",
"cpl-query": "src/cpl_query/cpl-query.json",
"cpl-translation": "src/cpl_translation/cpl-translation.json",
"set-version": "tools/set_version/set-version.json",
"set-pip-urls": "tools/set_pip_urls/set-pip-urls.json",
"unittests": "unittests/unittests/unittests.json",
"unittests_cli": "unittests/unittests_cli/unittests_cli.json",
"unittests_core": "unittests/unittests_core/unittests_core.json",
"unittests_query": "unittests/unittests_query/unittests_query.json",
"unittests_shared": "unittests/unittests_shared/unittests_shared.json",
"unittests_translation": "unittests/unittests_translation/unittests_translation.json"
},
"Scripts": {
"hello-world": "echo 'Hello World'",
"format": "echo 'Formatting:'; black ./",
"sv": "cpl set-version",
"set-version": "cpl run set-version --dev $ARGS; echo '';",
"spu": "cpl set-pip-urls",
"set-pip-urls": "cpl run set-pip-urls --dev $ARGS; echo '';",
"docs-build": "cpl format; echo 'Build Documentation'; cpl db-core; cpl db-discord; cpl db-query; cpl db-translation; cd docs/; make clean; make html;",
"db-core": "cd docs/; sphinx-apidoc -o source/ ../src/cpl_core; cd ../",
"db-discord": "cd docs/; sphinx-apidoc -o source/ ../src/cpl_discord; cd ../",
"db-query": "cd docs/; sphinx-apidoc -o source/ ../src/cpl_query; cd ../",
"db-translation": "cd docs/; sphinx-apidoc -o source/ ../src/cpl_translation; cd ../",
"db": "cpl docs-build",
"docs-open": "xdg-open $PWD/docs/build/html/index.html &",
"do": "cpl docs-open",
"test": "cpl run unittests",
"pre-build-all": "cpl sv $ARGS; cpl spu $ARGS;",
"build-all": "cpl build-cli; cpl build-core; cpl build-discord; cpl build-query; cpl build-translation; cpl build-set-pip-urls; cpl build-set-version",
"ba": "cpl build-all $ARGS",
"build-cli": "echo 'Build cpl-cli'; cd ./src/cpl_cli; cpl build; cd ../../;",
"build-core": "echo 'Build cpl-core'; cd ./src/cpl_core; cpl build; cd ../../;",
"build-discord": "echo 'Build cpl-discord'; cd ./src/cpl_discord; cpl build; cd ../../;",
"build-query": "echo 'Build cpl-query'; cd ./src/cpl_query; cpl build; cd ../../;",
"build-translation": "echo 'Build cpl-translation'; cd ./src/cpl_translation; cpl build; cd ../../;",
"build-set-pip-urls": "echo 'Build set-pip-urls'; cd ./tools/set_pip_urls; cpl build; cd ../../;",
"build-set-version": "echo 'Build set-version'; cd ./tools/set_version; cpl build; cd ../../;",
"pre-publish-all": "cpl sv $ARGS; cpl spu $ARGS;",
"publish-all": "cpl publish-cli; cpl publish-core; cpl publish-discord; cpl publish-query; cpl publish-translation;",
"pa": "cpl publish-all $ARGS",
"publish-cli": "echo 'Publish cpl-cli'; cd ./src/cpl_cli; cpl publish; cd ../../;",
"publish-core": "echo 'Publish cpl-core'; cd ./src/cpl_core; cpl publish; cd ../../;",
"publish-discord": "echo 'Publish cpl-discord'; cd ./src/cpl_discord; cpl publish; cd ../../;",
"publish-query": "echo 'Publish cpl-query'; cd ./src/cpl_query; cpl publish; cd ../../;",
"publish-translation": "echo 'Publish cpl-translation'; cd ./src/cpl_translation; cpl publish; cd ../../;",
"upload-prod-cli": "echo 'PROD Upload cpl-cli'; cpl upl-prod-cli;",
"upl-prod-cli": "twine upload -r pip.sh-edraft.de dist/cpl-cli/publish/setup/*",
"upload-prod-core": "echo 'PROD Upload cpl-core'; cpl upl-prod-core;",
"upl-prod-core": "twine upload -r pip.sh-edraft.de dist/cpl-core/publish/setup/*",
"upload-prod-discord": "echo 'PROD Upload cpl-discord'; cpl upl-prod-discord;",
"upl-prod-discord": "twine upload -r pip.sh-edraft.de dist/cpl-discord/publish/setup/*",
"upload-prod-query": "echo 'PROD Upload cpl-query'; cpl upl-prod-query;",
"upl-prod-query": "twine upload -r pip.sh-edraft.de dist/cpl-query/publish/setup/*",
"upload-prod-translation": "echo 'PROD Upload cpl-translation'; cpl upl-prod-translation;",
"upl-prod-translation": "twine upload -r pip.sh-edraft.de dist/cpl-translation/publish/setup/*",
"upload-exp-cli": "echo 'EXP Upload cpl-cli'; cpl upl-exp-cli;",
"upl-exp-cli": "twine upload -r pip-exp.sh-edraft.de dist/cpl-cli/publish/setup/*",
"upload-exp-core": "echo 'EXP Upload cpl-core'; cpl upl-exp-core;",
"upl-exp-core": "twine upload -r pip-exp.sh-edraft.de dist/cpl-core/publish/setup/*",
"upload-exp-discord": "echo 'EXP Upload cpl-discord'; cpl upl-exp-discord;",
"upl-exp-discord": "twine upload -r pip-exp.sh-edraft.de dist/cpl-discord/publish/setup/*",
"upload-exp-query": "echo 'EXP Upload cpl-query'; cpl upl-exp-query;",
"upl-exp-query": "twine upload -r pip-exp.sh-edraft.de dist/cpl-query/publish/setup/*",
"upload-exp-translation": "echo 'EXP Upload cpl-translation'; cpl upl-exp-translation;",
"upl-exp-translation": "twine upload -r pip-exp.sh-edraft.de dist/cpl-translation/publish/setup/*",
"upload-dev-cli": "echo 'DEV Upload cpl-cli'; cpl upl-dev-cli;",
"upl-dev-cli": "twine upload -r pip-dev.sh-edraft.de dist/cpl-cli/publish/setup/*",
"upload-dev-core": "echo 'DEV Upload cpl-core'; cpl upl-dev-core;",
"upl-dev-core": "twine upload -r pip-dev.sh-edraft.de dist/cpl-core/publish/setup/*",
"upload-dev-discord": "echo 'DEV Upload cpl-discord'; cpl upl-dev-discord;",
"upl-dev-discord": "twine upload -r pip-dev.sh-edraft.de dist/cpl-discord/publish/setup/*",
"upload-dev-query": "echo 'DEV Upload cpl-query'; cpl upl-dev-query;",
"upl-dev-query": "twine upload -r pip-dev.sh-edraft.de dist/cpl-query/publish/setup/*",
"upload-dev-translation": "echo 'DEV Upload cpl-translation'; cpl upl-dev-translation;",
"upl-dev-translation": "twine upload -r pip-dev.sh-edraft.de dist/cpl-translation/publish/setup/*",
"pre-deploy-prod": "cpl sv $ARGS; cpl spu --environment=production;",
"deploy-prod": "cpl deploy-prod-cli; cpl deploy-prod-core; cpl deploy-prod-discord; cpl deploy-prod-query; cpl deploy-prod-translation;",
"dp": "cpl deploy-prod $ARGS",
"deploy-prod-cli": "cpl publish-cli; cpl upload-prod-cli",
"deploy-prod-core": "cpl publish-core; cpl upload-prod-core",
"deploy-prod-query": "cpl publish-query; cpl upload-prod-query",
"deploy-prod-discord": "cpl publish-discord; cpl upload-prod-discord",
"deploy-prod-translation": "cpl publish-translation; cpl upload-prod-translation",
"pre-deploy-exp": "cpl sv $ARGS; cpl spu --environment=staging;",
"deploy-exp": "cpl deploy-exp-cli; cpl deploy-exp-core; cpl deploy-exp-discord; cpl deploy-exp-query; cpl deploy-exp-translation;",
"de": "cpl deploy-exp $ARGS",
"deploy-exp-cli": "cpl publish-cli; cpl upload-exp-cli",
"deploy-exp-core": "cpl publish-core; cpl upload-exp-core",
"deploy-exp-discord": "cpl publish-discord; cpl upload-exp-discord",
"deploy-exp-query": "cpl publish-query; cpl upload-exp-query",
"deploy-exp-translation": "cpl publish-translation; cpl upload-exp-translation",
"pre-deploy-dev": "cpl sv $ARGS; cpl spu --environment=development;",
"deploy-dev": "cpl deploy-dev-cli; cpl deploy-dev-core; cpl deploy-dev-discord; cpl deploy-dev-query; cpl deploy-dev-translation;",
"dd": "cpl deploy-dev $ARGS",
"deploy-dev-cli": "cpl publish-cli; cpl upload-dev-cli",
"deploy-dev-core": "cpl publish-core; cpl upload-dev-core",
"deploy-dev-discord": "cpl publish-discord; cpl upload-dev-discord",
"deploy-dev-query": "cpl publish-query; cpl upload-dev-query",
"deploy-dev-translation": "cpl publish-query; cpl upload-dev-translation",
"dev-install": "cpl di-core; cpl di-cli; cpl di-query; cpl di-translation;",
"di": "cpl dev-install",
"di-core": "pip install cpl-core --pre --upgrade --extra-index-url https://pip-dev.sh-edraft.de",
"di-cli": "pip install cpl-cli --pre --upgrade --extra-index-url https://pip-dev.sh-edraft.de",
"di-discord": "pip install cpl-discord --pre --upgrade --extra-index-url https://pip-dev.sh-edraft.de",
"di-query": "pip install cpl-query --pre --upgrade --extra-index-url https://pip-dev.sh-edraft.de",
"di-translation": "pip install cpl-translation --pre --upgrade --extra-index-url https://pip-dev.sh-edraft.de",
"prod-install": "cpl pi-core; cpl pi-cli; cpl pi-query; cpl pi-translation;",
"pi": "cpl prod-install",
"pi-core": "pip install cpl-core --pre --upgrade --extra-index-url https://pip.sh-edraft.de",
"pi-cli": "pip install cpl-cli --pre --upgrade --extra-index-url https://pip.sh-edraft.de",
"pi-discord": "pip install cpl-discord --pre --upgrade --extra-index-url https://pip.sh-edraft.de",
"pi-query": "pip install cpl-query --pre --upgrade --extra-index-url https://pip.sh-edraft.de",
"pi-translation": "pip install cpl-translation --pre --upgrade --extra-index-url https://pip.sh-edraft.de"
}
}
}

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,69 +0,0 @@
from cpl_cli.abc.project_type_abc import ProjectTypeABC
from cpl_cli.configuration import WorkspaceSettings
from cpl_core.utils import String
class Console(ProjectTypeABC):
def __init__(
self,
base_path: str,
project_name: str,
workspace: WorkspaceSettings,
use_application_api: bool,
use_startup: bool,
use_service_providing: bool,
use_async: bool,
project_file_data: dict,
):
from project_file import ProjectFile
from project_file_appsettings import ProjectFileAppsettings
from project_file_code_application import ProjectFileApplication
from project_file_code_main import ProjectFileMain
from project_file_code_startup import ProjectFileStartup
from project_file_readme import ProjectFileReadme
from project_file_license import ProjectFileLicense
from schematic_init import Init
ProjectTypeABC.__init__(
self,
base_path,
project_name,
workspace,
use_application_api,
use_startup,
use_service_providing,
use_async,
project_file_data,
)
project_path = f'{base_path}{String.convert_to_snake_case(project_name.split("/")[-1])}/'
self.add_template(ProjectFile(project_name.split("/")[-1], project_path, project_file_data))
if workspace is None:
self.add_template(ProjectFileLicense(""))
self.add_template(ProjectFileReadme(""))
self.add_template(Init("", "init", f"{base_path}tests/"))
self.add_template(Init("", "init", project_path))
self.add_template(ProjectFileAppsettings(project_path))
if use_application_api:
self.add_template(
ProjectFileApplication(project_path, use_application_api, use_startup, use_service_providing, use_async)
)
if use_startup:
self.add_template(
ProjectFileStartup(project_path, use_application_api, use_startup, use_service_providing, use_async)
)
self.add_template(
ProjectFileMain(
project_name.split("/")[-1],
project_path,
use_application_api,
use_startup,
use_service_providing,
use_async,
)
)

View File

@@ -1,13 +0,0 @@
import json
from cpl_cli.abc.file_template_abc import FileTemplateABC
class ProjectFile(FileTemplateABC):
def __init__(self, name: str, path: str, code: dict):
FileTemplateABC.__init__(self, "", path, "{}")
self._name = f"{name}.json"
self._code = code
def get_code(self) -> str:
return json.dumps(self._code, indent=2)

View File

@@ -1,29 +0,0 @@
import textwrap
from cpl_cli.abc.file_template_abc import FileTemplateABC
class ProjectFileAppsettings(FileTemplateABC):
def __init__(self, path: str):
FileTemplateABC.__init__(self, "", path, "{}")
self._name = "appsettings.json"
def get_code(self) -> str:
return textwrap.dedent(
"""\
{
"TimeFormatSettings": {
"DateFormat": "%Y-%m-%d",
"TimeFormat": "%H:%M:%S",
"DateTimeFormat": "%Y-%m-%d %H:%M:%S.%f",
"DateTimeLogFormat": "%Y-%m-%d_%H-%M-%S"
},
"LoggingSettings": {
"Path": "logs/",
"Filename": "log_$start_time.log",
"ConsoleLogLevel": "ERROR",
"FileLogLevel": "WARN"
}
}
"""
)

View File

@@ -1,56 +0,0 @@
from cpl_cli.abc.code_file_template_abc import CodeFileTemplateABC
class ProjectFileApplication(CodeFileTemplateABC):
def __init__(
self, path: str, use_application_api: bool, use_startup: bool, use_service_providing: bool, use_async: bool
):
CodeFileTemplateABC.__init__(
self, "application", path, "", use_application_api, use_startup, use_service_providing, use_async
)
def get_code(self) -> str:
import textwrap
if self._use_async:
return textwrap.dedent(
"""\
from cpl_core.application import ApplicationABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.console import Console
from cpl_core.dependency_injection import ServiceProviderABC
class Application(ApplicationABC):
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
ApplicationABC.__init__(self, config, services)
async def configure(self):
pass
async def main(self):
Console.write_line('Hello World')
"""
)
return textwrap.dedent(
"""\
from cpl_core.application import ApplicationABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.console import Console
from cpl_core.dependency_injection import ServiceProviderABC
class Application(ApplicationABC):
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
ApplicationABC.__init__(self, config, services)
def configure(self):
pass
def main(self):
Console.write_line('Hello World')
"""
)

View File

@@ -1,107 +0,0 @@
from cpl_cli.abc.code_file_template_abc import CodeFileTemplateABC
from cpl_core.utils import String
class ProjectFileMain(CodeFileTemplateABC):
def __init__(
self,
name: str,
path: str,
use_application_api: bool,
use_startup: bool,
use_service_providing: bool,
use_async: bool,
):
CodeFileTemplateABC.__init__(
self, "main", path, "", use_application_api, use_startup, use_service_providing, use_async
)
import textwrap
import_pkg = f"{String.convert_to_snake_case(name)}."
self._main_with_application_host_and_startup = textwrap.dedent(
f"""\
{"import asyncio" if self._use_async else ''}
from cpl_core.application import ApplicationBuilder
from {import_pkg}application import Application
from {import_pkg}startup import Startup
{self._async()}def main():
app_builder = ApplicationBuilder(Application)
app_builder.use_startup(Startup)
{"app: Application = await app_builder.build_async()" if self._use_async else ""}
{"await app.run_async()" if self._use_async else "app_builder.build().run()"}
if __name__ == '__main__':
{"asyncio.run(main())" if self._use_async else "main()"}
"""
)
self._main_with_application_base = textwrap.dedent(
f"""\
{"import asyncio" if self._use_async else ''}
from cpl_core.application import ApplicationBuilder
from {import_pkg}application import Application
{self._async()}def main():
app_builder = ApplicationBuilder(Application)
{"app: Application = await app_builder.build_async()" if self._use_async else ""}
{"await app.run_async()" if self._use_async else "app_builder.build().run()"}
if __name__ == '__main__':
{"asyncio.run(main())" if self._use_async else "main()"}
"""
)
self._main_with_dependency_injection = textwrap.dedent(
f"""\
{"import asyncio" if self._use_async else ''}
from cpl_core.application import ApplicationBuilder
{self._async()}def configure_configuration() -> ConfigurationABC:
config = Configuration()
return config
{self._async()}def configure_services(config: ConfigurationABC) -> ServiceProviderABC:
services = ServiceCollection(config)
return services.build_service_provider()
{self._async()}def main():
config = {self._async()}configure_configuration()
provider = {self._async()}configure_services(config)
Console.write_line('Hello World')
if __name__ == '__main__':
{"asyncio.run(main())" if self._use_async else "main()"}
"""
)
def _async(self) -> str:
if self._use_async:
return "async "
return ""
def get_code(self) -> str:
if self._use_application_api and self._use_startup:
return self._main_with_application_host_and_startup
if self._use_application_api:
return self._main_with_application_base
if self._use_service_providing:
return self._main_with_dependency_injection
return self._main_with_application_base

View File

@@ -1,34 +0,0 @@
from cpl_cli.abc.code_file_template_abc import CodeFileTemplateABC
class ProjectFileStartup(CodeFileTemplateABC):
def __init__(
self, path: str, use_application_api: bool, use_startup: bool, use_service_providing: bool, use_async: bool
):
CodeFileTemplateABC.__init__(
self, "startup", path, "", use_application_api, use_startup, use_service_providing, use_async
)
def get_code(self) -> str:
import textwrap
return textwrap.dedent(
"""\
from cpl_core.application import StartupABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceProviderABC, ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironment
class Startup(StartupABC):
def __init__(self):
StartupABC.__init__(self)
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironment) -> ConfigurationABC:
return configuration
def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC:
return services.build_service_provider()
"""
)

View File

@@ -1,66 +0,0 @@
from cpl_cli.abc.code_file_template_abc import CodeFileTemplateABC
class ProjectFileTestApplication(CodeFileTemplateABC):
def __init__(
self, path: str, use_application_api: bool, use_startup: bool, use_service_providing: bool, use_async: bool
):
CodeFileTemplateABC.__init__(
self, "application", path, "", use_application_api, use_startup, use_service_providing, use_async
)
def get_code(self) -> str:
import textwrap
if self._use_async:
return textwrap.dedent(
"""\
import unittest
from unittest import TestSuite
from cpl_core.application import ApplicationABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceProviderABC
from unittests.test_case import TestCase
class Application(ApplicationABC):
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
ApplicationABC.__init__(self, config, services)
self._suite: TestSuite = unittest.TestSuite()
async def configure(self):
self._suite.addTest(TestCase('test_equal'))
async def main(self):
runner = unittest.TextTestRunner()
runner.run(self._suite)
"""
)
return textwrap.dedent(
"""\
import unittest
from unittest import TestSuite
from cpl_core.application import ApplicationABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceProviderABC
from unittests.test_case import TestCase
class Application(ApplicationABC):
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
ApplicationABC.__init__(self, config, services)
self._suite: TestSuite = unittest.TestSuite()
def configure(self):
self._suite.addTest(TestCase('test_equal'))
def main(self):
runner = unittest.TextTestRunner()
runner.run(self._suite)
"""
)

View File

@@ -1,44 +0,0 @@
from cpl_cli.abc.code_file_template_abc import CodeFileTemplateABC
class ProjectFileTestCase(CodeFileTemplateABC):
def __init__(
self, path: str, use_application_api: bool, use_startup: bool, use_service_providing: bool, use_async: bool
):
CodeFileTemplateABC.__init__(
self, "test_case", path, "", use_application_api, use_startup, use_service_providing, use_async
)
def get_code(self) -> str:
import textwrap
if self._use_async:
return textwrap.dedent(
"""\
import unittest
class TestCase(unittest.TestCase):
async def setUp(self) -> None:
pass
async def test_equal(self):
self.assertEqual(True, True)
"""
)
return textwrap.dedent(
"""\
import unittest
class TestCase(unittest.TestCase):
def setUp(self) -> None:
pass
def test_equal(self):
self.assertEqual(True, True)
"""
)

View File

@@ -1,10 +0,0 @@
from cpl_cli.abc.file_template_abc import FileTemplateABC
class ProjectFileLicense(FileTemplateABC):
def __init__(self, path: str):
FileTemplateABC.__init__(self, "", path, "")
self._name = "LICENSE"
def get_code(self) -> str:
return self._code

View File

@@ -1,10 +0,0 @@
from cpl_cli.abc.file_template_abc import FileTemplateABC
class ProjectFileReadme(FileTemplateABC):
def __init__(self, path: str):
FileTemplateABC.__init__(self, "", path, "")
self._name = "README.md"
def get_code(self) -> str:
return self._code

View File

@@ -1,46 +0,0 @@
import os
from cpl_cli.abc.project_type_abc import ProjectTypeABC
from cpl_cli.configuration import WorkspaceSettings
from cpl_core.utils import String
class Library(ProjectTypeABC):
def __init__(
self,
base_path: str,
project_name: str,
workspace: WorkspaceSettings,
use_application_api: bool,
use_startup: bool,
use_service_providing: bool,
use_async: bool,
project_file_data: dict,
):
from project_file import ProjectFile
from project_file_readme import ProjectFileReadme
from project_file_license import ProjectFileLicense
from schematic_init import Init
from schematic_class import Class
ProjectTypeABC.__init__(
self,
base_path,
project_name,
workspace,
use_application_api,
use_startup,
use_service_providing,
use_async,
project_file_data,
)
project_path = f'{base_path}{String.convert_to_snake_case(project_name.split("/")[-1])}/'
self.add_template(ProjectFile(project_name.split("/")[-1], project_path, project_file_data))
if workspace is None:
self.add_template(ProjectFileLicense(""))
self.add_template(ProjectFileReadme(""))
self.add_template(Init("", "init", project_path))
self.add_template(Class("Class1", "class", project_path))

View File

@@ -1,64 +0,0 @@
import os
from cpl_cli.abc.project_type_abc import ProjectTypeABC
from cpl_cli.configuration import WorkspaceSettings
from cpl_core.utils import String
class Unittest(ProjectTypeABC):
def __init__(
self,
base_path: str,
project_name: str,
workspace: WorkspaceSettings,
use_application_api: bool,
use_startup: bool,
use_service_providing: bool,
use_async: bool,
project_file_data: dict,
):
from project_file import ProjectFile
from project_file_code_application import ProjectFileApplication
from project_file_code_main import ProjectFileMain
from project_file_code_test_case import ProjectFileTestCase
from project_file_readme import ProjectFileReadme
from project_file_license import ProjectFileLicense
from schematic_init import Init
ProjectTypeABC.__init__(
self,
base_path,
project_name,
workspace,
use_application_api,
use_startup,
use_service_providing,
use_async,
project_file_data,
)
project_path = f'{base_path}{String.convert_to_snake_case(project_name.split("/")[-1])}/'
self.add_template(ProjectFile(project_name.split("/")[-1], project_path, project_file_data))
if workspace is None:
self.add_template(ProjectFileLicense(""))
self.add_template(ProjectFileReadme(""))
self.add_template(Init("", "init", f"{base_path}tests/"))
self.add_template(Init("", "init", project_path))
self.add_template(
ProjectFileApplication(project_path, use_application_api, use_startup, use_service_providing, use_async)
)
self.add_template(
ProjectFileMain(
project_name.split("/")[-1],
project_path,
use_application_api,
use_startup,
use_service_providing,
use_async,
)
)
self.add_template(
ProjectFileTestCase(project_path, use_application_api, use_startup, use_service_providing, use_async)
)

View File

@@ -1,27 +0,0 @@
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
from cpl_core.utils import String
class ABC(GenerateSchematicABC):
def __init__(self, name: str, schematic: str, path: str):
GenerateSchematicABC.__init__(self, name, schematic, path)
self._class_name = name
if name != "":
self._class_name = f'{String.first_to_upper(name.replace(schematic, ""))}ABC'
def get_code(self) -> str:
code = """\
from abc import ABC, abstractmethod
class $Name(ABC):
@abstractmethod
def __init__(self): pass
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "abc", ["a", "A"])

View File

@@ -1,34 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class Application(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
from cpl_core.application import ApplicationABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.console import Console
from cpl_core.dependency_injection import ServiceProviderABC
class $Name(ApplicationABC):
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
ApplicationABC.__init__(self, config, services)
def configure(self):
pass
def main(self):
Console.write_line('Hello World')
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "application", ["app", "APP"])

View File

@@ -1,31 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
from cpl_core.utils import String
class ApplicationExtension(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
from cpl_core.application import ApplicationExtensionABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceProviderABC
class $Name(ApplicationExtensionABC):
def __init__(self):
ApplicationExtensionABC.__init__(self)
def run(self, config: ConfigurationABC, services: ServiceProviderABC):
pass
"""
x = self.build_code_str(code, Name=String.convert_to_camel_case(self._class_name))
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "application-extension", ["appex", "APPEX"])

View File

@@ -1,23 +0,0 @@
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
from cpl_core.utils import String
class Class(GenerateSchematicABC):
def __init__(self, name: str, schematic: str, path: str):
GenerateSchematicABC.__init__(self, name, schematic, path)
self._name = f"{String.convert_to_snake_case(name)}.py"
self._class_name = f"{String.first_to_upper(name)}"
def get_code(self) -> str:
code = """\
class $Name:
def __init__(self):
pass
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "class", ["c", "C"])

View File

@@ -1,35 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class ConfigModel(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
import traceback
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl_core.console import Console
class $Name(ConfigurationModelABC):
def __init__(self, atr: str = None):
ConfigurationModelABC.__init__(self)
self._atr = atr
@property
def atr(self) -> str:
return self._atr
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "settings", ["st", "ST"])

View File

@@ -1,25 +0,0 @@
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class Enum(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
import textwrap
code = textwrap.dedent(
"""\
from enum import Enum
class $Name(Enum):
atr = 0
"""
)
return self.build_code_str(code, Name=self._class_name)
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "enum", ["e", "E"])

View File

@@ -1,20 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class Init(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
self._name = f"__init__.py"
def get_code(self) -> str:
code = """\
# imports
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "init", [])

View File

@@ -1,27 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class Pipe(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
from cpl_core.pipes.pipe_abc import PipeABC
class $Name(PipeABC):
def __init__(self): pass
def transform(self, value: any, *args):
return value
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "pipe", ["p", "P"])

View File

@@ -1,46 +0,0 @@
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
from cpl_core.utils import String
class Schematic(GenerateSchematicABC):
def __init__(self, name: str, path: str, schematic: str):
GenerateSchematicABC.__init__(self, name, path, schematic)
self._name = f"schematic_{String.convert_to_snake_case(name)}.py"
self._path = ".cpl/"
self._class_name = String.convert_to_camel_case(name)
def get_code(self) -> str:
code = """\
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class $Name(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
import textwrap
code = textwrap.dedent(\"\"\"\\
from enum import Enum
class $Name(Enum):
atr = 0
\"\"\")
return self.build_code_str(code, Name=self._class_name)
@classmethod
def register(cls):
GenerateSchematicABC.register(
cls,
'$NameLower',
[]
)
"""
return self.build_code_str(code, Name=self._class_name, NameLower=self._class_name.lower())
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "schematic", ["scheme", "SCHEME"])

View File

@@ -1,22 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class Service(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
class $Name:
def __init__(self):
pass
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "service", ["s", "S"])

View File

@@ -1,34 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class Startup(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
from cpl_core.application import StartupABC
from cpl_core.configuration import ConfigurationABC
from cpl_core.dependency_injection import ServiceProviderABC, ServiceCollectionABC
from cpl_core.environment import ApplicationEnvironment
class $Name(StartupABC):
def __init__(self):
StartupABC.__init__(self)
def configure_configuration(self, configuration: ConfigurationABC, environment: ApplicationEnvironment) -> ConfigurationABC:
return configuration
def configure_services(self, services: ServiceCollectionABC, environment: ApplicationEnvironment) -> ServiceProviderABC:
return services.build_service_provider()
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "startup", ["stup", "STUP"])

View File

@@ -1,35 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
from cpl_core.utils import String
class StartupExtension(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
from cpl_core.application.startup_extension_abc import StartupExtensionABC
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.dependency_injection.service_collection_abc import ServiceCollectionABC
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
class $Name(StartupExtensionABC):
def __init__(self):
pass
def configure_configuration(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
pass
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironmentABC):
pass
"""
x = self.build_code_str(code, Name=String.convert_to_camel_case(self._class_name))
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "startup-extension", ["stupex", "STUPEX"])

View File

@@ -1,28 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
from cpl_core.utils import String
class TestCase(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
import unittest
class $Name(unittest.TestCase):
def setUp(self):
pass
def test_equal(self):
pass
"""
return self.build_code_str(code, Name=String.convert_to_camel_case(self._class_name))
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "test-case", ["tc", "TC"])

View File

@@ -1,28 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class Thread(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
import threading
class $Name(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
def run(self) -> None:
pass
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "thread", ["t", "T"])

View File

@@ -1,28 +0,0 @@
import textwrap
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
class Validator(GenerateSchematicABC):
def __init__(self, *args: str):
GenerateSchematicABC.__init__(self, *args)
def get_code(self) -> str:
code = """\
from cpl_core.configuration.validator_abc import ValidatorABC
class $Name(ValidatorABC):
def __init__(self):
ValidatorABC.__init__(self)
def validate(self) -> bool:
return True
"""
x = self.build_code_str(code, Name=self._class_name)
return x
@classmethod
def register(cls):
GenerateSchematicABC.register(cls, "validator", ["v", "V"])

View File

@@ -1,31 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
from .cli import CLI
from .command_abc import CommandABC
from .error import Error
from .main import main
from .startup import Startup
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli._templates"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli._templates.build"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,38 +0,0 @@
import textwrap
class InitTemplate:
@staticmethod
def get_init_py() -> str:
string = textwrap.dedent(
"""\
# -*- coding: utf-8 -*-
\"\"\"
$Name $Description
~~~~~~~~~~~~~~~~~~~
$LongDescription
:copyright: (c) $CopyrightDate $CopyrightName
:license: $LicenseDescription
\"\"\"
__title__ = "$Title"
__author__ = "$Author"
__license__ = "$LicenseName"
__copyright__ = "Copyright (c) $CopyrightDate $CopyrightName"
__version__ = "$Version"
from collections import namedtuple
$Imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="$Major", minor="$Minor", micro="$Micro")
"""
)
return string

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli._templates.publish"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,33 +0,0 @@
import textwrap
class SetupTemplate:
@staticmethod
def get_setup_py() -> str:
string = textwrap.dedent(
"""\
\"\"\"
This file is generated by CPL CLI
\"\"\"
import setuptools
setuptools.setup(
name='$Name',
version='$Version',
packages=$Packages,
url='$URL',
license='$LicenseName',
author='$Author',
author_email='$AuthorMail',
include_package_data=$IncludePackageData,
description='$Description',
python_requires='$PyRequires',
install_requires=$Dependencies,
entry_points=$EntryPoints,
package_data=$PackageData
)
"""
)
return string

View File

@@ -1,22 +0,0 @@
from abc import ABC, abstractmethod
class TemplateFileABC(ABC):
@abstractmethod
def __init__(self):
pass
@property
@abstractmethod
def name(self) -> str:
pass
@property
@abstractmethod
def path(self) -> str:
pass
@property
@abstractmethod
def value(self) -> str:
pass

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.abc"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,23 +0,0 @@
from abc import ABC, abstractmethod
from cpl_cli.abc.file_template_abc import FileTemplateABC
from cpl_core.utils import String
class CodeFileTemplateABC(FileTemplateABC):
@abstractmethod
def __init__(
self,
name: str,
path: str,
code: str,
use_application_api: bool,
use_startup: bool,
use_service_providing: bool,
use_async: bool,
):
FileTemplateABC.__init__(self, name, path, code)
self._use_application_api = use_application_api
self._use_startup = use_startup
self._use_service_providing = use_service_providing
self._use_async = use_async

View File

@@ -1,34 +0,0 @@
from abc import ABC, abstractmethod
from cpl_core.utils import String
class FileTemplateABC(ABC):
@abstractmethod
def __init__(self, name: str, path: str, code: str):
self._name = f"{String.convert_to_snake_case(name)}.py"
self._path = path
self._code = code
def __repr__(self):
return f"<{type(self).__name__} {self._path}{self._name}>"
@property
def name(self) -> str:
return self._name
@property
def path(self) -> str:
return self._path
@path.setter
def path(self, value: str):
self._path = value
@property
def value(self) -> str:
return self.get_code()
@abstractmethod
def get_code(self) -> str:
pass

View File

@@ -1,40 +0,0 @@
import textwrap
from abc import abstractmethod
from string import Template
from cpl_cli.abc.file_template_abc import FileTemplateABC
from cpl_cli.configuration.schematic_collection import SchematicCollection
from cpl_core.utils import String
class GenerateSchematicABC(FileTemplateABC):
def __init__(self, name: str, schematic: str, path: str):
FileTemplateABC.__init__(self, name, path, "")
self._name = f"{String.convert_to_snake_case(name)}_{schematic}.py"
if schematic in name.lower():
self._name = f"{String.convert_to_snake_case(name)}.py"
self._class_name = name
if name != "":
self._class_name = f"{String.first_to_upper(name)}{String.first_to_upper(schematic)}"
if schematic in name.lower():
self._class_name = f"{String.first_to_upper(name)}"
@property
def class_name(self) -> str:
return self._class_name
@abstractmethod
def get_code(self) -> str:
pass
@classmethod
def build_code_str(cls, code: str, **kwargs) -> str:
text = textwrap.dedent(code)
return Template(text).substitute(**kwargs)
@classmethod
@abstractmethod
def register(cls, *args):
SchematicCollection.register(*args)

View File

@@ -1,36 +0,0 @@
from abc import ABC, abstractmethod
from typing import Optional
from cpl_cli.abc.file_template_abc import FileTemplateABC
from cpl_cli.configuration import WorkspaceSettings
class ProjectTypeABC(ABC):
@abstractmethod
def __init__(
self,
base_path: str,
project_name: str,
workspace: Optional[WorkspaceSettings],
use_application_api: bool,
use_startup: bool,
use_service_providing: bool,
use_async: bool,
project_file_data: dict,
):
self._templates: list[FileTemplateABC] = []
self._base_path = base_path
self._project_name = project_name
self._workspace = workspace
self._use_application_api = use_application_api
self._use_startup = use_startup
self._use_service_providing = use_service_providing
self._use_async = use_async
self._project_file_data = project_file_data
@property
def templates(self) -> list[FileTemplateABC]:
return self._templates
def add_template(self, t: FileTemplateABC):
self._templates.append(t)

View File

@@ -1,5 +0,0 @@
{
"CLI": {
"PipPath": "https://pip.sh-edraft.de"
}
}

View File

@@ -1,46 +0,0 @@
import sys
import traceback
from cpl_cli.error import Error
from cpl_core.application.application_abc import ApplicationABC
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_core.dependency_injection.service_provider_abc import ServiceProviderABC
class CLI(ApplicationABC):
def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
"""
CPL CLI
"""
ApplicationABC.__init__(self, config, services)
self._options: list[str] = []
def configure(self):
pass
def main(self):
"""
Entry point of the CPL CLI
:return:
"""
try:
result = self._configuration.parse_console_arguments(self._services)
if result:
Console.write_line()
return
if len(self._configuration.additional_arguments) == 0:
Error.error("Expected command")
return
unexpected_arguments = ", ".join(self._configuration.additional_arguments)
Error.error(f"Unexpected argument(s): {unexpected_arguments}")
Console.write_line()
except KeyboardInterrupt:
Console.write_line()
sys.exit()
except Exception as e:
Console.error(str(e), traceback.format_exc())
sys.exit()

View File

@@ -1,14 +0,0 @@
from typing import Optional
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
class CLISettings(ConfigurationModelABC):
def __init__(self, pip_path: str = None):
ConfigurationModelABC.__init__(self)
self._pip_path: Optional[str] = pip_path
@property
def pip_path(self) -> str:
return self._pip_path

View File

@@ -1,5 +0,0 @@
from enum import Enum
class CLISettingsNameEnum(Enum):
pip_path = "PipPath"

View File

@@ -1,32 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.command"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
from .build_service import BuildService
from .generate_service import GenerateService
from .help_service import HelpService
from .new_service import NewService
from .publish_service import PublishService
from .version_service import VersionService
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,138 +0,0 @@
import json
import os
import textwrap
from typing import Optional
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.configuration.settings_helper import SettingsHelper
from cpl_cli.configuration.workspace_settings import WorkspaceSettings
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
class AddService(CommandABC):
def __init__(self, config: ConfigurationABC, workspace: WorkspaceSettings):
"""
Service for CLI command add
"""
CommandABC.__init__(self)
self._config = config
self._workspace = workspace
self._is_simulation = False
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Adds a project reference to given project.
Usage: cpl add <source-project> <target-project>
Arguments:
source-project: Name of the project to which the reference has to be
target-project: Name of the project to be referenced
"""
)
def _edit_project_file(self, source: str, project_settings: ProjectSettings, build_settings: BuildSettings):
if self._is_simulation:
return
with open(source, "w") as file:
file.write(
json.dumps(
{
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(build_settings),
},
indent=2,
)
)
file.close()
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
if "simulate" in args:
args.remove("simulate")
Console.write_line("Running in simulation mode:")
self._is_simulation = True
if len(args) == 0:
Console.error("Expected source and target project")
return
elif len(args) == 1:
Console.error("Expected target project")
return
elif len(args) > 2:
Console.error(f'Unexpected argument(s): {", ".join(args[2:])}')
return
# file names
source = args[0]
target = args[1]
# validation flags
is_invalid_source = False
is_invalid_target = source == target
if not is_invalid_target:
if self._workspace is None:
is_invalid_source = not os.path.isfile(source)
is_invalid_target = not os.path.isfile(target)
else:
if source not in self._workspace.projects:
is_invalid_source = True
else:
source = self._workspace.projects[source]
if target not in self._workspace.projects:
is_invalid_target = True
else:
target = self._workspace.projects[target]
# load project-name.json
self._config.add_json_file(source, optional=True, output=False)
project_settings: Optional[ProjectSettings] = self._config.get_configuration(ProjectSettings)
build_settings: Optional[BuildSettings] = self._config.get_configuration(BuildSettings)
if project_settings is None or build_settings is None:
is_invalid_source = True
if is_invalid_source:
Console.error(f"Invalid source: {source}")
return
if is_invalid_target or source == target or not os.path.isfile(target):
Console.error(f"Invalid target: {target}")
return
if self._workspace is None:
target = f"../{target}"
else:
target = target.replace("src", "..")
if target in build_settings.project_references:
Console.error(f"Project reference already exists.")
return
build_settings.project_references.append(target)
Console.spinner(
f"Editing {source}",
self._edit_project_file,
source,
project_settings,
build_settings,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)

View File

@@ -1,32 +0,0 @@
import textwrap
from cpl_cli.command_abc import CommandABC
from cpl_cli.publish.publisher_abc import PublisherABC
class BuildService(CommandABC):
def __init__(self, publisher: PublisherABC):
"""
Service for the CLI command build
:param publisher:
"""
CommandABC.__init__(self)
self._publisher = publisher
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Copies an python app into an output directory named build/ at the given output path. Must be executed within a CPL workspace or project directory
Usage: cpl build
"""
)
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
self._publisher.build()

View File

@@ -1,49 +0,0 @@
import os
import subprocess
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration.workspace_settings import WorkspaceSettings
class CustomScriptService(CommandABC):
def __init__(self, config: ConfigurationABC, env: ApplicationEnvironmentABC, ws: WorkspaceSettings):
"""
Service for CLI scripts
"""
CommandABC.__init__(self)
self._config = config
self._env = env
self._workspace = ws
@property
def help_message(self) -> str:
return ""
def execute(self, args: list[str]):
cmd = self._config.get_configuration("ACTIVE_EXECUTABLE")
wd = self._config.get_configuration("PATH_WORKSPACE")
if wd is not None:
self._env.set_working_directory(wd)
for script in self._workspace.scripts:
if script != cmd:
continue
command = ""
external_args = self._config.get_configuration("ARGS")
if external_args is not None:
command += f'ARGS="{external_args}";'
command += self._workspace.scripts[script]
env_vars = os.environ
env_vars["CPL_ARGS"] = " ".join(args)
try:
subprocess.run(command, shell=True if os.name == "posix" else None)
except Exception as e:
Console.error(str(e))

View File

@@ -1,223 +0,0 @@
import importlib
import os
import sys
import textwrap
import traceback
from cpl_cli.abc.generate_schematic_abc import GenerateSchematicABC
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration import WorkspaceSettings
from cpl_cli.configuration.schematic_collection import SchematicCollection
from cpl_cli.helper.dependencies import Dependencies
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.utils.string import String
class GenerateService(CommandABC):
def __init__(
self,
configuration: ConfigurationABC,
workspace: WorkspaceSettings,
):
"""
Service for the CLI command generate
:param configuration:
"""
CommandABC.__init__(self)
self._config = configuration
self._workspace = workspace
self._config = configuration
self._env = self._config.environment
self._schematics = {}
self._schematic_classes = set()
for package_name, version in Dependencies.get_cpl_packages():
if package_name == "cpl-cli":
continue
package = importlib.import_module(String.convert_to_snake_case(package_name))
self._read_custom_schematics_from_path(os.path.dirname(package.__file__))
self._read_custom_schematics_from_path(self._env.working_directory)
self._read_custom_schematics_from_path(self._env.runtime_directory)
if len(self._schematic_classes) == 0:
Console.error(f"No schematics found in template directory: .cpl")
sys.exit()
known_schematics = []
for schematic in self._schematic_classes:
known_schematics.append(schematic.__name__)
schematic.register()
self._schematics = SchematicCollection.get_schematics()
@property
def help_message(self) -> str:
schematics = []
for schematic in self._schematics:
aliases = "|".join(self._schematics[schematic]["Aliases"])
schematic_str = schematic
if len(aliases) > 0:
schematic_str = f"{schematic} ({aliases})"
schematics.append(schematic_str)
help_msg = textwrap.dedent(
"""\
Generate a file based on schematic.
Usage: cpl generate <schematic> <name>
Arguments:
schematic: The schematic to generate.
name: The name of the generated file
Schematics:"""
)
for schematic in schematics:
help_msg += f"\n {schematic}"
return help_msg
def _read_custom_schematics_from_path(self, path: str):
if not os.path.exists(os.path.join(path, ".cpl")):
return
sys.path.insert(0, os.path.join(path, ".cpl"))
for r, d, f in os.walk(os.path.join(path, ".cpl")):
for file in f:
if not file.startswith("schematic_") or not file.endswith(".py"):
continue
try:
exec(open(os.path.join(r, file), "r").read())
self._schematic_classes.update(GenerateSchematicABC.__subclasses__())
except Exception as e:
Console.error(str(e), traceback.format_exc())
sys.exit(-1)
@staticmethod
def _create_file(file_path: str, value: str):
"""
Creates the given file with content
:param file_path:
:param value:
:return:
"""
with open(file_path, "w") as template:
template.write(value)
template.close()
def _create_init_files(
self, file_path: str, template: GenerateSchematicABC, class_name: str, schematic: str, rel_path: str
):
if not os.path.isdir(os.path.dirname(file_path)):
os.makedirs(os.path.dirname(file_path))
directory = ""
for subdir in template.path.split("/"):
directory = os.path.join(directory, subdir)
if subdir == "src":
continue
file = self._schematics["init"]["Template"](class_name, "init", rel_path)
if os.path.exists(os.path.join(os.path.abspath(directory), file.name)):
continue
Console.spinner(
f"Creating {os.path.abspath(directory)}/{file.name}",
self._create_file,
os.path.join(os.path.abspath(directory), file.name),
file.get_code(),
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
def _generate(self, schematic: str, name: str, template: type):
"""
Generates files by given schematic, name and template
:param schematic:
:param name:
:param template:
:return:
"""
class_name = name
rel_path = ""
if "/" in name:
parts = name.split("/")
rel_path = "/".join(parts[:-1])
class_name = parts[len(parts) - 1]
if self._workspace is not None and parts[0] in self._workspace.projects:
rel_path = os.path.join(os.path.dirname(self._workspace.projects[parts[0]]), *parts[1:-1])
template = template(class_name, String.convert_to_snake_case(schematic), rel_path)
file_path = os.path.join(self._env.working_directory, template.path, template.name)
self._create_init_files(file_path, template, class_name, schematic, rel_path)
if os.path.isfile(file_path):
Console.error(f"{String.first_to_upper(schematic)} already exists!\n")
sys.exit()
message = f"Creating {self._env.working_directory}/{template.path}/{template.name}"
if template.path == "":
message = f"Creating {self._env.working_directory}/{template.name}"
Console.spinner(
message,
self._create_file,
file_path,
template.get_code(),
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
def _get_schematic_by_alias(self, schematic: str) -> str:
for key in self._schematics:
if schematic in self._schematics[key]["Aliases"]:
return key
return schematic
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
schematic = None
value = None
for s in self._schematics:
value = self._config.get_configuration(s)
if value is not None:
schematic = s
break
if (
schematic is None
and len(args) >= 1
and (args[0] in self._schematics or self._get_schematic_by_alias(args[0]) != args[0])
):
schematic = self._get_schematic_by_alias(args[0])
self._config.add_configuration(schematic, args[1])
value = args[1]
if schematic is None:
Console.error(f"Schematic not found")
Console.write_line(self.help_message)
sys.exit()
name = value
if name is None:
name = Console.read(f"Name for the {args[0]}: ")
if schematic in self._schematics:
s = self._schematics[schematic]
self._generate(schematic, name, s["Template"])
else:
self._help("Usage: cpl generate <schematic> [options]")
Console.write_line()
sys.exit()

View File

@@ -1,67 +0,0 @@
import sys
import textwrap
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.dependency_injection.service_provider_abc import ServiceProviderABC
from cpl_cli.command_abc import CommandABC
class HelpService(CommandABC):
def __init__(self, services: ServiceProviderABC):
"""
Service for CLI command help
"""
CommandABC.__init__(self)
self._services = services
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Lists available command and their short descriptions.
Usage: cpl help
"""
)
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
if len(args) > 0:
Console.error(f'Unexpected argument(s): {", ".join(args)}')
sys.exit()
Console.write_line("Available Commands:")
commands = [
["add (a|a)", "Adds a project reference to given project."],
[
"build (b|B)",
"Prepares files for publish into an output directory named dist/ at the given output path. Must be executed from within a workspace directory.",
],
["generate (g|G)", "Generate a new file."],
["help (h|H)", "Lists available command and their short descriptions."],
[
"install (i|I)",
"With argument installs packages to project, without argument installs project dependencies.",
],
["new (n|N)", "Creates new CPL project."],
[
"publish (p|P)",
"Prepares files for publish into an output directory named dist/ at the given output path and executes setup.py. Must be executed from within a library workspace directory.",
],
["remove (r|R)", "Removes a project from workspace."],
["start (s|S)", "Starts CPL project, restarting on file changes."],
["uninstall (ui|UI)", "Uninstalls packages from project."],
["update (u|u)", "Update CPL and project dependencies."],
["version (v|V)", "Outputs CPL CLI version."],
]
for name, description in commands:
Console.set_foreground_color(ForegroundColorEnum.blue)
Console.write(f"\n\t{name} ")
Console.set_foreground_color(ForegroundColorEnum.default)
Console.write(f"{description}")
Console.write_line("\nRun 'cpl <command> --help' for command specific information's\n")

View File

@@ -1,281 +0,0 @@
import json
import os
import subprocess
import textwrap
import time
from packaging import version
from cpl_cli.cli_settings import CLISettings
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.configuration.settings_helper import SettingsHelper
from cpl_cli.configuration.venv_helper_service import VenvHelper
from cpl_cli.error import Error
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_core.utils.pip import Pip
class InstallService(CommandABC):
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
build_settings: BuildSettings,
project_settings: ProjectSettings,
cli_settings: CLISettings,
):
"""
Service for the CLI command install
:param config:
:param env:
:param build_settings:
:param project_settings:
:param cli_settings:
"""
CommandABC.__init__(self)
self._config = config
self._env = env
self._build_settings = build_settings
self._project_settings = project_settings
self._cli_settings = cli_settings
self._is_simulation = False
self._is_virtual = False
self._is_dev = False
self._project_file = f"{self._project_settings.name}.json"
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Installs given package via pip
Usage: cpl install <package>
Arguments:
package The package to install
"""
)
def _wait(self, t: int, *args, source: str = None, stdout=None, stderr=None):
time.sleep(t)
def _install_project(self):
"""
Installs dependencies of CPl project
:return:
"""
if self._project_settings is None or self._build_settings is None:
Error.error("The command requires to be run in an CPL project, but a project could not be found.")
return
if self._project_settings.dependencies is None:
Error.error(f"Found invalid dependencies in {self._project_file}.")
return
for dependency in self._project_settings.dependencies:
Console.spinner(
f"Installing: {dependency}",
Pip.install if not self._is_virtual else self._wait,
dependency if not self._is_virtual else 2,
"--upgrade",
source=self._cli_settings.pip_path,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
local_package = Pip.get_package(dependency)
if local_package is None:
Error.warn(f"Installation of package {dependency} failed!")
return
for dependency in self._project_settings.dev_dependencies:
Console.spinner(
f"Installing dev: {dependency}",
Pip.install if not self._is_virtual else self._wait,
dependency if not self._is_virtual else 2,
"--upgrade",
source=self._cli_settings.pip_path,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
local_package = Pip.get_package(dependency)
if local_package is None:
Error.warn(f"Installation of package {dependency} failed!")
return
if not self._is_virtual:
Pip.reset_executable()
def _install_package(self, package: str):
"""
Installs given package
:param package:
:return:
"""
is_already_in_project = False
if self._project_settings is None or self._build_settings is None:
Error.error("The command requires to be run in an CPL project, but a project could not be found.")
return
if self._project_settings.dependencies is None:
Error.error(f"Found invalid dependencies in {self._project_file}.")
return
package_version = ""
name = package
if "==" in package:
name = package.split("==")[0]
package_version = package.split("==")[1]
elif ">=" in package:
name = package.split(">=")[0]
package_version = package.split(">=")[1]
elif "<=" in package:
name = package.split("<=")[0]
package_version = package.split("<=")[1]
to_remove_list = []
deps = self._project_settings.dependencies
if self._is_dev:
deps = self._project_settings.dev_dependencies
for dependency in deps:
dependency_version = ""
if "==" in dependency:
dependency_version = dependency.split("==")[1]
elif "<=" in dependency:
dependency_version = dependency.split("<=")[1]
elif ">=" in dependency:
dependency_version = dependency.split(">=")[1]
if name in dependency:
if package_version != "" and version.parse(package_version) != version.parse(dependency_version):
to_remove_list.append(dependency)
break
else:
is_already_in_project = True
for to_remove in to_remove_list:
if self._is_dev:
self._project_settings.dev_dependencies.remove(to_remove)
else:
self._project_settings.dependencies.remove(to_remove)
local_package = Pip.get_package(package)
if local_package is not None and local_package in self._project_settings.dependencies:
Error.warn(f"Package {local_package} is already installed.")
return
elif is_already_in_project:
Error.warn(f"Package {package} is already installed.")
return
Console.spinner(
f"Installing: {package}" if not self._is_dev else f"Installing dev: {package}",
Pip.install if not self._is_virtual else self._wait,
package if not self._is_virtual else 2,
source=self._cli_settings.pip_path,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
if self._is_virtual:
new_package = name
else:
new_package = Pip.get_package(name)
if (
new_package is None
or "==" in package
and version.parse(package.split("==")[1]) != version.parse(new_package.split("==")[1])
or "<=" in package
and version.parse(package.split("<=")[1]) != version.parse(new_package.split("<=")[1])
or ">=" in package
and version.parse(package.split(">=")[1]) != version.parse(new_package.split(">=")[1])
):
Console.error(f"Installation of package {package} failed")
return
if not is_already_in_project:
new_name = package
if "==" in new_package or ">=" in new_package or "<=" in new_package:
new_name = new_package
elif "==" in name or ">=" in name or "<=" in name:
new_name = name
if "/" in new_name:
new_name = new_name.split("/")[0]
if "\r" in new_name:
new_name = new_name.replace("\r", "")
if self._is_dev:
self._project_settings.dev_dependencies.append(new_name)
else:
self._project_settings.dependencies.append(new_name)
if not self._is_simulation:
config = {
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(self._project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings),
}
with open(os.path.join(self._env.working_directory, self._project_file), "w") as project_file:
project_file.write(json.dumps(config, indent=2))
project_file.close()
Pip.reset_executable()
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
if "dev" in args:
self._is_dev = True
args.remove("dev")
if "virtual" in args:
self._is_virtual = True
args.remove("virtual")
Console.write_line("Running in virtual mode:")
if "simulate" in args:
self._is_simulation = True
args.remove("simulate")
Console.write_line("Running in simulation mode:")
if "cpl-prod" in args:
args.remove("cpl-prod")
self._cli_settings.from_dict({"PipPath": "https://pip.sh-edraft.de"})
if "cpl-exp" in args:
args.remove("cpl-exp")
self._cli_settings.from_dict({"PipPath": "https://pip-exp.sh-edraft.de"})
if "cpl-dev" in args:
args.remove("cpl-dev")
self._cli_settings.from_dict({"PipPath": "https://pip-dev.sh-edraft.de"})
VenvHelper.init_venv(self._is_virtual, self._env, self._project_settings.python_executable)
if len(args) == 0:
self._install_project()
else:
self._install_package(args[0])
if not self._is_virtual:
Pip.reset_executable()

View File

@@ -1,362 +0,0 @@
import importlib
import os
import sys
import textwrap
import traceback
from typing import Optional
from packaging import version
import cpl_cli
import cpl_core
from cpl_cli.abc.project_type_abc import ProjectTypeABC
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration import VersionSettings
from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.configuration.project_type_enum import ProjectTypeEnum
from cpl_cli.configuration.settings_helper import SettingsHelper
from cpl_cli.configuration.venv_helper_service import VenvHelper
from cpl_cli.configuration.workspace_settings import WorkspaceSettings
from cpl_cli.helper.dependencies import Dependencies
from cpl_cli.source_creator.template_builder import TemplateBuilder
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.utils.string import String
class NewService(CommandABC):
def __init__(self, configuration: ConfigurationABC):
"""
Service for the CLI command new
:param configuration:
"""
CommandABC.__init__(self)
self._config = configuration
self._env = self._config.environment
self._workspace: WorkspaceSettings = self._config.get_configuration(WorkspaceSettings)
self._project_dict = {}
self._build_dict = {}
self._project_name = ""
self._python_executable = ""
self._project_type_classes = set()
self._name: str = ""
self._rel_path: str = ""
self._project_type: ProjectTypeEnum = ProjectTypeEnum.console
self._use_nothing: bool = False
self._use_application_api: bool = False
self._use_startup: bool = False
self._use_service_providing: bool = False
self._use_async: bool = False
self._use_venv: bool = False
self._use_base: bool = False
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Generates a workspace and initial project or add a project to workspace.
Usage: cpl new <type> <name>
Arguments:
type The project type of the initial project
name Name of the workspace or the project
Types:
console (c|C)
library (l|L)
unittest (ut|UT)
"""
)
def _create_project_settings(self):
self._project_name = os.path.basename(self._name)
self._python_executable = ProjectSettings(
python_path={sys.platform: "../../venv/" if self._use_venv else ""}
).python_executable
self._rel_path = os.path.dirname(self._name)
self._project_dict = SettingsHelper.get_project_settings_dict(
ProjectSettings(
os.path.basename(self._name),
VersionSettings("0", "0", "0"),
"",
"",
"",
"",
"",
"",
"",
"",
"",
[f"cpl-core>={version.parse(cpl_core.__version__)}"],
[f"cpl-cli>={version.parse(cpl_cli.__version__)}"],
f'>={sys.version.split(" ")[0]}',
{sys.platform: "../../venv/" if self._use_venv else ""},
None,
[],
)
)
def _create_build_settings(self, project_type: str):
self._build_dict = SettingsHelper.get_build_settings_dict(
BuildSettings(
ProjectTypeEnum[project_type],
"",
"../../dist",
f"{String.convert_to_snake_case(self._project_name)}.main",
self._project_name,
False,
[],
["*/__pycache__", "*/logs", "*/tests"],
{},
[],
)
)
def _create_project_json(self):
"""
Creates cpl.json content
:return:
"""
self._project_json = {ProjectSettings.__name__: self._project_dict, BuildSettings.__name__: self._build_dict}
def _get_project_path(self) -> Optional[str]:
"""
Gets project path
:return:
"""
if self._workspace is None:
project_path = os.path.join(self._env.working_directory, self._rel_path, self._project_name)
else:
base = "" if self._use_base else "src"
project_path = os.path.join(
self._env.working_directory, base, self._rel_path, String.convert_to_snake_case(self._project_name)
)
if os.path.isdir(project_path) and len(os.listdir(project_path)) > 0:
Console.write_line(project_path)
Console.error("Project path is not empty\n")
return None
return project_path
def _get_project_information(self, project_type: str):
"""
Gets project information's from user
:return:
"""
is_unittest = project_type == "unittest"
is_library = project_type == "library"
if is_library:
return
if (
self._use_application_api
or self._use_startup
or self._use_service_providing
or self._use_async
or self._use_nothing
):
Console.set_foreground_color(ForegroundColorEnum.default)
Console.write_line("Skipping question due to given flags")
return
if not is_unittest and not is_library:
self._use_application_api = Console.read("Do you want to use application base? (y/n) ").lower() == "y"
if not is_unittest and self._use_application_api:
self._use_startup = Console.read("Do you want to use startup? (y/n) ").lower() == "y"
if not is_unittest and not self._use_application_api:
self._use_service_providing = Console.read("Do you want to use service providing? (y/n) ").lower() == "y"
if not self._use_async:
self._use_async = Console.read("Do you want to use async? (y/n) ").lower() == "y"
Console.set_foreground_color(ForegroundColorEnum.default)
def _create_venv(self):
project = self._project_name
if self._workspace is not None:
project = self._workspace.default_project
if self._env.working_directory.endswith(project):
project = ""
if self._workspace is None and self._use_base:
project = f"{self._rel_path}/{project}"
VenvHelper.init_venv(
False,
self._env,
self._python_executable,
explicit_path=os.path.join(
self._env.working_directory, project, self._python_executable.replace("../", "")
),
)
def _read_custom_project_types_from_path(self, path: str):
if not os.path.exists(os.path.join(path, ".cpl")):
return
sys.path.insert(0, os.path.join(path, ".cpl"))
for r, d, f in os.walk(os.path.join(path, ".cpl")):
for file in f:
if file.startswith("project_file_") or not file.startswith("project_") or not file.endswith(".py"):
continue
try:
exec(open(os.path.join(r, file), "r").read())
self._project_type_classes.update(ProjectTypeABC.__subclasses__())
except Exception as e:
Console.error(str(e), traceback.format_exc())
sys.exit(-1)
def _create_project(self, project_type: str):
for package_name in Dependencies.get_cpl_packages():
if package_name == "cpl-cli":
continue
package = importlib.import_module(String.convert_to_snake_case(package_name[0]))
self._read_custom_project_types_from_path(os.path.dirname(package.__file__))
self._read_custom_project_types_from_path(self._env.working_directory)
self._read_custom_project_types_from_path(self._env.runtime_directory)
if len(self._project_type_classes) == 0:
Console.error(f"No project types found in template directory: .cpl")
sys.exit()
project_class = None
known_project_types = []
for p in self._project_type_classes:
known_project_types.append(p.__name__)
if p.__name__.lower() != project_type and p.__name__.lower()[0] != project_type[0]:
continue
project_class = p
if project_class is None:
Console.error(f"Project type {project_type} not found in template directory: .cpl/")
sys.exit()
project_type = String.convert_to_snake_case(project_class.__name__)
self._create_project_settings()
self._create_build_settings(project_type)
self._create_project_json()
path = self._get_project_path()
if path is None:
return
self._get_project_information(project_type)
project_name = self._project_name
if self._rel_path != "":
project_name = f"{self._rel_path}/{project_name}"
base = "src/"
split_project_name = project_name.split("/")
if self._use_base and len(split_project_name) > 0:
base = f"{split_project_name[0]}/"
project = project_class(
base if self._workspace is not None else "src/",
project_name,
self._workspace,
self._use_application_api,
self._use_startup,
self._use_service_providing,
self._use_async,
self._project_json,
)
if self._workspace is None:
TemplateBuilder.create_workspace(
f"{project_name}/cpl-workspace.json",
project_name.split("/")[-1],
{
project_name: f'{base if self._workspace is not None else "src/"}{String.convert_to_snake_case(project_name)}/{project_name}.json'
},
{},
)
else:
self._workspace.projects[
project_name
] = f'{base if self._workspace is not None else "src/"}{String.convert_to_snake_case(project_name)}/{project_name}.json'
TemplateBuilder.create_workspace(
"cpl-workspace.json", self._workspace.default_project, self._workspace.projects, self._workspace.scripts
)
for template in project.templates:
rel_base = "/".join(project_name.split("/")[:-1])
template_path_base = template.path.split("/")[0]
if not self._use_base and rel_base != "" and template_path_base != "" and template_path_base != rel_base:
template.path = template.path.replace(f"{template_path_base}/", f"{template_path_base}/{rel_base}/")
if template.name.endswith(f'{project_name.split("/")[-1]}.json'):
pass
file_path = os.path.join(project_name if self._workspace is None else "", template.path, template.name)
Console.spinner(
f"Creating {file_path}",
TemplateBuilder.build,
file_path,
template,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
if self._use_venv:
self._create_venv()
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
if "nothing" in args:
self._use_nothing = True
self._use_async = False
self._use_application_api = False
self._use_startup = False
self._use_service_providing = False
if "async" in args:
args.remove("async")
if "application-base" in args:
args.remove("application-base")
if "startup" in args:
args.remove("startup")
if "service-providing" in args:
args.remove("service-providing")
if "async" in args:
self._use_async = True
args.remove("async")
if "application-base" in args:
self._use_application_api = True
args.remove("application-base")
if "startup" in args:
self._use_startup = True
args.remove("startup")
if "service-providing" in args:
self._use_service_providing = True
args.remove("service-providing")
if "venv" in args:
self._use_venv = True
args.remove("venv")
if "base" in args:
self._use_base = True
args.remove("base")
if len(args) <= 1:
Console.error(f"Project type not found")
Console.write_line(self.help_message)
return
self._name = args[1]
self._create_project(args[0])

View File

@@ -1,32 +0,0 @@
import textwrap
from cpl_cli.command_abc import CommandABC
from cpl_cli.publish.publisher_abc import PublisherABC
class PublishService(CommandABC):
def __init__(self, publisher: PublisherABC):
"""
Service for the CLI command publish
:param publisher:
"""
CommandABC.__init__(self)
self._publisher = publisher
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Prepares files for publish into an output directory named dist/ at the given output path and executes setup.py.
Usage: cpl publish
"""
)
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
self._publisher.publish()

View File

@@ -1,172 +0,0 @@
import os
import shutil
import json
import textwrap
from cpl_cli.configuration.settings_helper import SettingsHelper
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration import (
WorkspaceSettings,
WorkspaceSettingsNameEnum,
BuildSettingsNameEnum,
ProjectSettings,
BuildSettings,
)
class RemoveService(CommandABC):
def __init__(self, config: ConfigurationABC, env: ApplicationEnvironmentABC):
"""
Service for CLI command remove
:param config:
:param env:
"""
CommandABC.__init__(self)
self._config = config
self._env = env
self._workspace: WorkspaceSettings = self._config.get_configuration(WorkspaceSettings)
self._is_simulation = False
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Removes a project from workspace.
Usage: cpl remove <project>
Arguments:
project The name of the project to delete
"""
)
def _create_file(self, file_name: str, content: dict):
if self._is_simulation:
return
if not os.path.isabs(file_name):
file_name = os.path.abspath(file_name)
path = os.path.dirname(file_name)
if not os.path.isdir(path):
os.makedirs(path)
with open(file_name, "w") as project_json:
project_json.write(json.dumps(content, indent=2))
project_json.close()
def _remove_sources(self, path: str):
if self._is_simulation:
return
shutil.rmtree(path)
def _create_workspace(self, path: str):
ws_dict = {
WorkspaceSettings.__name__: {
WorkspaceSettingsNameEnum.default_project.value: self._workspace.default_project,
WorkspaceSettingsNameEnum.projects.value: self._workspace.projects,
WorkspaceSettingsNameEnum.scripts.value: self._workspace.scripts,
}
}
self._create_file(path, ws_dict)
def _get_project_settings(self, project: str) -> dict:
with open(os.path.join(os.getcwd(), self._workspace.projects[project]), "r", encoding="utf-8") as cfg:
# load json
project_json = json.load(cfg)
cfg.close()
return project_json
def _write_project_settings(self, project: str, project_settings: dict, build_settings: dict):
with open(os.path.join(os.getcwd(), self._workspace.projects[project]), "w", encoding="utf-8") as file:
file.write(
json.dumps(
{ProjectSettings.__name__: project_settings, BuildSettings.__name__: build_settings}, indent=2
)
)
file.close()
def _find_deps_in_projects(self, project_name: str, rel_path: str):
for project in self._workspace.projects:
if project == project_name:
continue
project_settings = self._get_project_settings(project)
if (
BuildSettings.__name__ not in project_settings
or BuildSettingsNameEnum.project_references.value not in project_settings[BuildSettings.__name__]
):
continue
ref_to_delete = ""
for ref in project_settings[BuildSettings.__name__][BuildSettingsNameEnum.project_references.value]:
if os.path.basename(ref) == f"{project_name}.json":
ref_to_delete = ref
if (
ref_to_delete
not in project_settings[BuildSettings.__name__][BuildSettingsNameEnum.project_references.value]
):
continue
project_settings[BuildSettings.__name__][BuildSettingsNameEnum.project_references.value].remove(
ref_to_delete
)
Console.spinner(
f"Removing {project_name} from {project}",
self._write_project_settings,
project,
project_settings[ProjectSettings.__name__],
project_settings[BuildSettings.__name__],
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
if "simulate" in args:
args.remove("simulate")
Console.write_line("Running in simulation mode:")
self._is_simulation = True
project_name = args[0]
if project_name not in self._workspace.projects:
Console.error(f"Project {project_name} not found in workspace.")
return
if project_name == self._workspace.default_project:
Console.error(f"Project {project_name} is the default project.")
return
src_path = os.path.dirname(self._workspace.projects[project_name])
Console.spinner(
f"Removing {src_path}",
self._remove_sources,
os.path.abspath(src_path),
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
self._find_deps_in_projects(project_name, src_path)
del self._workspace.projects[project_name]
path = "cpl-workspace.json"
Console.spinner(
f"Changing {path}",
self._create_workspace,
path,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)

View File

@@ -1,125 +0,0 @@
import os
import sys
import textwrap
from cpl_cli.error import Error
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration.workspace_settings import WorkspaceSettings
from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.live_server.start_executable import StartExecutable
from cpl_cli.publish.publisher_service import PublisherService
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_core.dependency_injection.service_provider_abc import ServiceProviderABC
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_core.utils.string import String
class RunService(CommandABC):
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
services: ServiceProviderABC,
project_settings: ProjectSettings,
build_settings: BuildSettings,
workspace: WorkspaceSettings,
publisher: PublisherService,
):
"""
Service for the CLI command start
:param config:
:param env:
:param services:
:param project_settings:
:param build_settings:
:param workspace:
"""
CommandABC.__init__(self)
self._config = config
self._env = env
self._services = services
self._project_settings = project_settings
self._build_settings = build_settings
self._workspace = workspace
self._publisher = publisher
self._src_dir = os.path.join(self._env.working_directory, self._build_settings.source_path)
self._is_dev = False
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Starts your application.
Usage: cpl run
"""
)
def _set_project_by_args(self, name: str):
if self._workspace is None:
Error.error("The command requires to be run in an CPL workspace, but a workspace could not be found.")
sys.exit()
if name not in self._workspace.projects:
Error.error(f"Project {name} not found in workspace")
sys.exit()
project_path = self._workspace.projects[name]
self._config.add_configuration(ProjectSettings, None)
self._config.add_configuration(BuildSettings, None)
working_directory = self._config.get_configuration("PATH_WORKSPACE")
if working_directory is not None:
self._env.set_working_directory(working_directory)
json_file = os.path.join(self._env.working_directory, project_path)
self._config.add_json_file(json_file, optional=True, output=False)
self._project_settings: ProjectSettings = self._config.get_configuration(ProjectSettings)
self._build_settings: BuildSettings = self._config.get_configuration(BuildSettings)
if self._project_settings is None or self._build_settings is None:
Error.error(f"Project {name} not found")
sys.exit()
self._src_dir = os.path.dirname(json_file)
def _build(self):
if self._is_dev:
return
self._env.set_working_directory(self._src_dir)
self._publisher.build()
self._env.set_working_directory(self._src_dir)
self._src_dir = os.path.abspath(
os.path.join(
self._src_dir,
self._build_settings.output_path,
self._project_settings.name,
"build",
String.convert_to_snake_case(self._project_settings.name),
)
)
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
if "dev" in args:
self._is_dev = True
args.remove("dev")
if len(args) >= 1:
self._set_project_by_args(args[0])
args.remove(args[0])
self._build()
start_service = StartExecutable(self._env, self._build_settings)
start_service.run(args, self._project_settings.python_executable, self._src_dir, output=False)
Console.write_line()

View File

@@ -1,32 +0,0 @@
import textwrap
from cpl_cli.command_abc import CommandABC
from cpl_cli.live_server.live_server_service import LiveServerService
class StartService(CommandABC):
def __init__(self, live_server: LiveServerService):
"""
Service for the CLI command start
:param live_server:
"""
CommandABC.__init__(self)
self._live_server = live_server
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Starts your application, restarting on file changes.
Usage: cpl start
"""
)
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
self._live_server.start(args)

View File

@@ -1,140 +0,0 @@
import json
import os
import subprocess
import textwrap
import time
from cpl_cli.configuration.venv_helper_service import VenvHelper
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_core.utils.pip import Pip
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.configuration.settings_helper import SettingsHelper
class UninstallService(CommandABC):
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
build_settings: BuildSettings,
project_settings: ProjectSettings,
):
"""
Service for the CLI command uninstall
:param config:
:param env:
:param build_settings:
:param project_settings:
"""
CommandABC.__init__(self)
self._config = config
self._env = env
self._build_settings = build_settings
self._project_settings = project_settings
self._is_simulating = False
self._is_virtual = False
self._is_dev = False
self._project_file = f"{self._project_settings.name}.json"
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Uninstalls given package via pip
Usage: cpl uninstall <package>
Arguments:
package The package to uninstall
"""
)
def _wait(self, t: int, *args, source: str = None, stdout=None, stderr=None):
time.sleep(t)
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
if len(args) == 0:
Console.error(f"Expected package")
Console.error(f"Usage: cpl uninstall <package>")
return
if "dev" in args:
self._is_dev = True
args.remove("dev")
if "--virtual" in args:
self._is_virtual = True
args.remove("--virtual")
Console.write_line("Running in virtual mode:")
if "--simulate" in args:
self._is_virtual = True
args.remove("--simulate")
Console.write_line("Running in simulation mode:")
VenvHelper.init_venv(self._is_virtual, self._env, self._project_settings.python_executable)
package = args[0]
is_in_dependencies = False
if not self._is_virtual:
pip_package = Pip.get_package(package)
else:
pip_package = package
deps = self._project_settings.dependencies
if self._is_dev:
deps = self._project_settings.dev_dependencies
for dependency in deps:
if package in dependency:
is_in_dependencies = True
package = dependency
if not is_in_dependencies and pip_package is None:
Console.error(f"Package {package} not found")
return
elif not is_in_dependencies and pip_package is not None:
package = pip_package
Console.spinner(
f"Uninstalling: {package}" if not self._is_dev else f"Uninstalling dev: {package}",
Pip.uninstall if not self._is_virtual else self._wait,
package if not self._is_virtual else 2,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
deps = self._project_settings.dependencies
if self._is_dev:
deps = self._project_settings.dev_dependencies
if package in deps:
deps.remove(package)
if not self._is_simulating:
config = {
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(self._project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings),
}
with open(os.path.join(self._env.working_directory, self._project_file), "w") as project_file:
project_file.write(json.dumps(config, indent=2))
project_file.close()
Console.write_line(f"Removed {package}")
if not self._is_virtual:
Pip.reset_executable()

View File

@@ -1,228 +0,0 @@
import json
import os
import subprocess
import textwrap
from cpl_cli.configuration.venv_helper_service import VenvHelper
from cpl_cli.migrations.base.migration_service_abc import MigrationServiceABC
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_core.utils.pip import Pip
from cpl_cli.cli_settings import CLISettings
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration import BuildSettings
from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.configuration.settings_helper import SettingsHelper
class UpdateService(CommandABC):
def __init__(
self,
config: ConfigurationABC,
env: ApplicationEnvironmentABC,
build_settings: BuildSettings,
project_settings: ProjectSettings,
cli_settings: CLISettings,
migrations: MigrationServiceABC,
):
"""
Service for the CLI command update
:param config:
:param env:
:param build_settings:
:param project_settings:
:param cli_settings:
"""
CommandABC.__init__(self)
self._config = config
self._env = env
self._build_settings = build_settings
self._project_settings = project_settings
self._cli_settings = cli_settings
self._migrations = migrations
self._is_simulation = False
self._project_file = f"{self._project_settings.name}.json"
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Updates the CPL and project dependencies.
Usage: cpl update
"""
)
def _collect_project_dependencies(self) -> list[tuple]:
"""
Collects project dependencies
:return:
"""
dependencies = []
for package in [*self._project_settings.dependencies, *self._project_settings.dev_dependencies]:
name = package
if "==" in package:
name = package.split("==")[0]
elif ">=" in package:
name = package.split(">=")[0]
elif "<=" in package:
name = package.split("<=")[0]
dependencies.append((package, name))
return dependencies
def _update_project_dependencies(self, dependencies):
"""
Updates project dependencies
:return:
"""
for package, name in dependencies:
Pip.install(
name,
"--upgrade",
"--upgrade-strategy",
"eager",
source=self._cli_settings.pip_path,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
new_package = Pip.get_package(name)
if new_package is None:
Console.error(f"Update for package {package} failed")
continue
self._project_json_update_dependency(package, new_package)
def _check_project_dependencies(self):
"""
Checks project dependencies for updates
:return:
"""
dependencies = Console.spinner(
"Collecting installed dependencies",
self._collect_project_dependencies,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
Console.spinner(
"Updating installed dependencies",
self._update_project_dependencies,
dependencies,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
if "cpl-cli" in [y for x, y in dependencies]:
import cpl_cli
Console.spinner(
"Running migrations",
self._migrations.migrate_from,
cpl_cli.__version__,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
Console.write_line(f"Found {len(self._project_settings.dependencies)} dependencies.")
@staticmethod
def _check_outdated():
"""
Checks for outdated packages in project
:return:
"""
table_str: bytes = Console.spinner(
"Analyzing for available package updates",
Pip.get_outdated,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
table = str(table_str, "utf-8").split("\n")
if len(table) > 1 and table[0] != "":
Console.write_line("\tAvailable updates for packages:")
for row in table:
Console.write_line(f"\t{row}")
Console.set_foreground_color(ForegroundColorEnum.yellow)
Console.write_line(f"\tUpdate with {Pip.get_executable()} -m pip install --upgrade <package>")
Console.set_foreground_color(ForegroundColorEnum.default)
def _save_formatted_package_name_to_deps_collection(self, deps: [str], old_package: str, new_package: str):
if old_package not in deps:
return
initial_package = new_package
if "/" in new_package:
new_package = new_package.split("/")[0]
if "\r" in new_package:
new_package = new_package.replace("\r", "")
if new_package == old_package:
return
index = deps.index(old_package)
deps[index] = new_package
def _project_json_update_dependency(self, old_package: str, new_package: str):
"""
Writes new package version to project.json
:param old_package:
:param new_package:
:return:
"""
if self._is_simulation:
return
self._save_formatted_package_name_to_deps_collection(
self._project_settings.dependencies, old_package, new_package
)
self._save_formatted_package_name_to_deps_collection(
self._project_settings.dev_dependencies, old_package, new_package
)
config = {
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(self._project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings),
}
with open(os.path.join(self._env.working_directory, self._project_file), "w") as project:
project.write(json.dumps(config, indent=2))
project.close()
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
if "simulate" in args:
args.remove("simulate")
Console.write_line("Running in simulation mode:")
self._is_simulation = True
if "cpl-prod" in args:
args.remove("cpl-prod")
self._cli_settings.from_dict({"PipPath": "https://pip.sh-edraft.de"})
if "cpl-exp" in args:
args.remove("cpl-exp")
self._cli_settings.from_dict({"PipPath": "https://pip-exp.sh-edraft.de"})
if "cpl-dev" in args:
args.remove("cpl-dev")
self._cli_settings.from_dict({"PipPath": "https://pip-dev.sh-edraft.de"})
VenvHelper.init_venv(False, self._env, self._project_settings.python_executable)
self._check_project_dependencies()
self._check_outdated()
Pip.reset_executable()

View File

@@ -1,49 +0,0 @@
import pkgutil
import sys
import platform
import pkg_resources
import textwrap
import cpl_cli
from cpl_cli.helper.dependencies import Dependencies
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_cli.command_abc import CommandABC
class VersionService(CommandABC):
def __init__(self):
"""
Service for the CLI command version
"""
CommandABC.__init__(self)
@property
def help_message(self) -> str:
return textwrap.dedent(
"""\
Lists the version of CPL, CPL CLI and all installed packages from pip.
Usage: cpl version
"""
)
def execute(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
Console.set_foreground_color(ForegroundColorEnum.yellow)
Console.banner("CPL CLI")
Console.set_foreground_color(ForegroundColorEnum.default)
if "__version__" in dir(cpl_cli):
Console.write_line(f"Common Python library CLI: ")
Console.write(cpl_cli.__version__)
Console.write_line(f"Python: ")
Console.write(f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")
Console.write_line(f"OS: {platform.system()} {platform.processor()}")
Console.write_line("\nCPL packages:")
Console.table(["Name", "Version"], Dependencies.get_cpl_packages())
Console.write_line("\nPython packages:")
Console.table(["Name", "Version"], Dependencies.get_packages())

View File

@@ -1,26 +0,0 @@
from abc import abstractmethod, ABC
from cpl_core.configuration.argument_executable_abc import ArgumentExecutableABC
from cpl_core.console import Console
class CommandABC(ArgumentExecutableABC):
@abstractmethod
def __init__(self):
ABC.__init__(self)
@property
@abstractmethod
def help_message(self) -> str:
pass
@abstractmethod
def execute(self, args: list[str]):
pass
def run(self, args: list[str]):
if "help" in args:
Console.write_line(self.help_message)
return
self.execute(args)

View File

@@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.configuration"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
from .build_settings import BuildSettings
from .build_settings_name_enum import BuildSettingsNameEnum
from .project_settings import ProjectSettings
from .project_settings_name_enum import ProjectSettingsNameEnum
from .version_settings import VersionSettings
from .version_settings_name_enum import VersionSettingsNameEnum
from .workspace_settings import WorkspaceSettings
from .workspace_settings_name_enum import WorkspaceSettingsNameEnum
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,95 +0,0 @@
import sys
import traceback
from typing import Optional
from cpl_cli.configuration.build_settings_name_enum import BuildSettingsNameEnum
from cpl_cli.configuration.project_type_enum import ProjectTypeEnum
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
class BuildSettings(ConfigurationModelABC):
def __init__(
self,
project_type: ProjectTypeEnum = None,
source_path: str = None,
output_path: str = None,
main: str = None,
entry_point: str = None,
include_package_data: bool = None,
included: list = None,
excluded: list = None,
package_data: dict = None,
project_references: list = None,
):
ConfigurationModelABC.__init__(self)
self._project_type: Optional[ProjectTypeEnum] = project_type
self._source_path: Optional[str] = source_path
self._output_path: Optional[str] = output_path
self._main: Optional[str] = main
self._entry_point: Optional[str] = entry_point
self._include_package_data: Optional[bool] = include_package_data
self._included: Optional[list[str]] = included
self._excluded: Optional[list[str]] = excluded
self._package_data: Optional[dict[str, list[str]]] = package_data
self._project_references: Optional[list[str]] = [] if project_references is None else project_references
if sys.platform == "win32":
self._source_path = str(self._source_path).replace("/", "\\")
self._output_path = str(self._output_path).replace("/", "\\")
# windows paths for excluded files
excluded = []
for ex in self._excluded:
excluded.append(str(ex).replace("/", "\\"))
self._excluded = excluded
# windows paths for included files
included = []
for inc in self._included:
included.append(str(inc).replace("/", "\\"))
self._included = included
@property
def project_type(self):
return self._project_type
@property
def source_path(self) -> str:
return self._source_path
@property
def output_path(self) -> str:
return self._output_path
@property
def main(self) -> str:
return self._main
@property
def entry_point(self) -> str:
return self._entry_point
@property
def include_package_data(self) -> bool:
return self._include_package_data
@property
def included(self) -> list[str]:
return self._included
@property
def excluded(self) -> list[str]:
return self._excluded
@property
def package_data(self) -> dict[str, list[str]]:
return self._package_data
@property
def project_references(self) -> list[str]:
return self._project_references

View File

@@ -1,14 +0,0 @@
from enum import Enum
class BuildSettingsNameEnum(Enum):
project_type = "ProjectType"
source_path = "SourcePath"
output_path = "OutputPath"
main = "Main"
entry_point = "EntryPoint"
include_package_data = "IncludePackageData"
included = "Included"
excluded = "Excluded"
package_data = "PackageData"
project_references = "ProjectReferences"

View File

@@ -1,132 +0,0 @@
import os
import sys
from typing import Optional
from cpl_cli.configuration.project_settings_name_enum import ProjectSettingsNameEnum
from cpl_cli.configuration.version_settings import VersionSettings
from cpl_cli.error import Error
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
class ProjectSettings(ConfigurationModelABC):
def __init__(
self,
name: str = None,
version: VersionSettings = None,
author: str = None,
author_email: str = None,
description: str = None,
long_description: str = None,
url: str = None,
copyright_date: str = None,
copyright_name: str = None,
license_name: str = None,
license_description: str = None,
dependencies: list = None,
dev_dependencies: list = None,
python_version: str = None,
python_path: dict = None,
python_executable: str = None,
classifiers: list = None,
):
ConfigurationModelABC.__init__(self)
self._name: Optional[str] = name
self._version: Optional[VersionSettings] = version
self._author: Optional[str] = author
self._author_email: Optional[str] = author_email
self._description: Optional[str] = description
self._long_description: Optional[str] = long_description
self._url: Optional[str] = url
self._copyright_date: Optional[str] = copyright_date
self._copyright_name: Optional[str] = copyright_name
self._license_name: Optional[str] = license_name
self._license_description: Optional[str] = license_description
self._dependencies: Optional[list[str]] = [] if dependencies is None else dependencies
self._dev_dependencies: Optional[list[str]] = [] if dev_dependencies is None else dev_dependencies
self._python_version: Optional[str] = python_version
self._python_path: Optional[str] = python_path
self._python_executable: Optional[str] = python_executable
self._classifiers: Optional[list[str]] = [] if classifiers is None else classifiers
if python_path is not None and sys.platform in python_path:
path = f"{python_path[sys.platform]}"
if path == "" or path is None:
Error.warn(f"{ProjectSettingsNameEnum.python_path.value} not set")
path = sys.executable
else:
if not path.endswith("bin/python"):
path = os.path.join(path, "bin/python")
else:
path = sys.executable
self._python_executable = path
@property
def name(self):
return self._name
@property
def version(self) -> VersionSettings:
return self._version
@property
def author(self) -> str:
return self._author
@property
def author_email(self) -> str:
return self._author_email
@property
def description(self) -> str:
return self._description
@property
def long_description(self) -> str:
return self._long_description
@property
def url(self) -> str:
return self._url
@property
def copyright_date(self) -> str:
return self._copyright_date
@property
def copyright_name(self) -> str:
return self._copyright_name
@property
def license_name(self) -> str:
return self._license_name
@property
def license_description(self) -> str:
return self._license_description
@property
def dependencies(self) -> list[str]:
return self._dependencies
@property
def dev_dependencies(self) -> list[str]:
return self._dev_dependencies
@property
def python_version(self) -> str:
return self._python_version
@property
def python_path(self) -> str:
return self._python_path
@property
def python_executable(self) -> str:
return self._python_executable
@property
def classifiers(self) -> list[str]:
return self._classifiers

View File

@@ -1,20 +0,0 @@
from enum import Enum
class ProjectSettingsNameEnum(Enum):
name = "Name"
version = "Version"
author = "Author"
author_email = "AuthorEmail"
description = "Description"
long_description = "LongDescription"
url = "URL"
copyright_date = "CopyrightDate"
copyright_name = "CopyrightName"
license_name = "LicenseName"
license_description = "LicenseDescription"
dependencies = "Dependencies"
dev_dependencies = "DevDependencies"
python_version = "PythonVersion"
python_path = "PythonPath"
classifiers = "Classifiers"

View File

@@ -1,8 +0,0 @@
from enum import Enum
class ProjectTypeEnum(Enum):
console = "console"
library = "library"
unittest = "unittest"
discord_bot = "discord-bot"

View File

@@ -1,13 +0,0 @@
from cpl_core.utils import String
class SchematicCollection:
_schematics: dict = {}
@classmethod
def register(cls, template: type, schematic: str, aliases: list[str]):
cls._schematics[schematic] = {"Template": template, "Aliases": aliases}
@classmethod
def get_schematics(cls) -> dict:
return cls._schematics

View File

@@ -1,47 +0,0 @@
from cpl_cli.configuration.version_settings_name_enum import VersionSettingsNameEnum
from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.build_settings_name_enum import BuildSettingsNameEnum
from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.configuration.project_settings_name_enum import ProjectSettingsNameEnum
class SettingsHelper:
@staticmethod
def get_project_settings_dict(project: ProjectSettings) -> dict:
return {
ProjectSettingsNameEnum.name.value: project.name,
ProjectSettingsNameEnum.version.value: {
VersionSettingsNameEnum.major.value: project.version.major,
VersionSettingsNameEnum.minor.value: project.version.minor,
VersionSettingsNameEnum.micro.value: project.version.micro,
},
ProjectSettingsNameEnum.author.value: project.author,
ProjectSettingsNameEnum.author_email.value: project.author_email,
ProjectSettingsNameEnum.description.value: project.description,
ProjectSettingsNameEnum.long_description.value: project.long_description,
ProjectSettingsNameEnum.url.value: project.url,
ProjectSettingsNameEnum.copyright_date.value: project.copyright_date,
ProjectSettingsNameEnum.copyright_name.value: project.copyright_name,
ProjectSettingsNameEnum.license_name.value: project.license_name,
ProjectSettingsNameEnum.license_description.value: project.license_description,
ProjectSettingsNameEnum.dependencies.value: project.dependencies,
ProjectSettingsNameEnum.dev_dependencies.value: project.dev_dependencies,
ProjectSettingsNameEnum.python_version.value: project.python_version,
ProjectSettingsNameEnum.python_path.value: project.python_path,
ProjectSettingsNameEnum.classifiers.value: project.classifiers,
}
@staticmethod
def get_build_settings_dict(build: BuildSettings) -> dict:
return {
BuildSettingsNameEnum.project_type.value: build.project_type.value,
BuildSettingsNameEnum.source_path.value: build.source_path,
BuildSettingsNameEnum.output_path.value: build.output_path,
BuildSettingsNameEnum.main.value: build.main,
BuildSettingsNameEnum.entry_point.value: build.entry_point,
BuildSettingsNameEnum.include_package_data.value: build.include_package_data,
BuildSettingsNameEnum.included.value: build.included,
BuildSettingsNameEnum.excluded.value: build.excluded,
BuildSettingsNameEnum.package_data.value: build.package_data,
BuildSettingsNameEnum.project_references.value: build.project_references,
}

View File

@@ -1,42 +0,0 @@
import os
import subprocess
import sys
from cpl_cli.configuration import ProjectSettings
from cpl_core.environment import ApplicationEnvironmentABC
from cpl_core.utils import Pip
from cpl_core.console import Console, ForegroundColorEnum
class VenvHelper:
@staticmethod
def init_venv(is_virtual: bool, env: ApplicationEnvironmentABC, python_executable: str, explicit_path=None):
if is_virtual:
return
venv_path = os.path.abspath(os.path.join(env.working_directory, python_executable, "../../"))
if explicit_path is not None:
venv_path = os.path.abspath(explicit_path)
if not os.path.exists(venv_path):
Console.spinner(
f"Creating venv: {venv_path}",
VenvHelper.create_venv,
venv_path,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
Pip.set_executable(python_executable)
@staticmethod
def create_venv(path):
subprocess.run(
[sys.executable, "-m", "venv", os.path.abspath(os.path.join(path, "../../"))],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
stdin=subprocess.DEVNULL,
)

View File

@@ -1,42 +0,0 @@
from typing import Optional
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl_cli.configuration.version_settings_name_enum import VersionSettingsNameEnum
class VersionSettings(ConfigurationModelABC):
def __init__(self, major: str = None, minor: str = None, micro: str = None):
ConfigurationModelABC.__init__(self)
self._major: Optional[str] = major
self._minor: Optional[str] = minor
self._micro: Optional[str] = micro if micro != "" else None
@property
def major(self) -> str:
return self._major
@property
def minor(self) -> str:
return self._minor
@property
def micro(self) -> str:
return self._micro
def to_str(self) -> str:
if self._micro is None:
return f"{self._major}.{self._minor}"
else:
return f"{self._major}.{self._minor}.{self._micro}"
def to_dict(self) -> dict:
version = {
VersionSettingsNameEnum.major.value: self._major,
VersionSettingsNameEnum.minor.value: self._minor,
}
if self._micro is not None:
version[VersionSettingsNameEnum.micro.value] = self._micro
return version

View File

@@ -1,7 +0,0 @@
from enum import Enum
class VersionSettingsNameEnum(Enum):
major = "Major"
minor = "Minor"
micro = "Micro"

View File

@@ -1,32 +0,0 @@
import traceback
from typing import Optional
from cpl_cli.configuration.workspace_settings_name_enum import WorkspaceSettingsNameEnum
from cpl_core.configuration.configuration_model_abc import ConfigurationModelABC
from cpl_core.console import Console
class WorkspaceSettings(ConfigurationModelABC):
def __init__(
self,
default_project: str = None,
projects: dict = None,
scripts: dict = None,
):
ConfigurationModelABC.__init__(self)
self._default_project: Optional[str] = default_project
self._projects: dict[str, str] = {} if projects is None else projects
self._scripts: dict[str, str] = {} if scripts is None else scripts
@property
def default_project(self) -> str:
return self._default_project
@property
def projects(self) -> dict[str, str]:
return self._projects
@property
def scripts(self):
return self._scripts

View File

@@ -1,7 +0,0 @@
from enum import Enum
class WorkspaceSettingsNameEnum(Enum):
default_project = "DefaultProject"
projects = "Projects"
scripts = "Scripts"

View File

@@ -1,51 +0,0 @@
{
"ProjectSettings": {
"Name": "cpl-cli",
"Version": {
"Major": "2024",
"Minor": "7",
"Micro": "0"
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",
"Description": "CPL CLI",
"LongDescription": "CPL Command Line Interface",
"URL": "https://www.sh-edraft.de",
"CopyrightDate": "2020 - 2024",
"CopyrightName": "sh-edraft.de",
"LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [
"cpl-core>=2024.6.2024.07.0"
],
"DevDependencies": [],
"PythonVersion": ">=3.12",
"PythonPath": {
"linux": "../../venv"
},
"Classifiers": []
},
"BuildSettings": {
"ProjectType": "console",
"SourcePath": "",
"OutputPath": "../../dist",
"Main": "cpl_cli.main",
"EntryPoint": "cpl",
"IncludePackageData": true,
"Included": [
"*/_templates"
],
"Excluded": [
"*/__pycache__",
"*/logs",
"*/tests"
],
"PackageData": {
"cpl_cli": [
"*.json",
".cpl/*.py"
]
},
"ProjectReferences": []
}
}

View File

@@ -1,15 +0,0 @@
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.console.console import Console
class Error:
@staticmethod
def error(message: str):
Console.error(message)
Console.error("Run 'cpl help'\n")
@staticmethod
def warn(message: str):
Console.set_foreground_color(ForegroundColorEnum.yellow)
Console.write_line(message, "\n")
Console.set_foreground_color(ForegroundColorEnum.default)

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.helper"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,22 +0,0 @@
import pkg_resources
class Dependencies:
_packages = []
_cpl_packages = []
_dependencies = dict(tuple(str(ws).split()) for ws in pkg_resources.working_set)
for p in _dependencies:
if str(p).startswith("cpl-"):
_cpl_packages.append([p, _dependencies[p]])
continue
_packages.append([p, _dependencies[p]])
@classmethod
def get_cpl_packages(cls) -> list[list]:
return cls._cpl_packages
@classmethod
def get_packages(cls) -> list[list]:
return cls._packages

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.live_server"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,129 +0,0 @@
import os
import time
from contextlib import suppress
import psutil as psutil
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
from cpl_cli.publish import PublisherService
from cpl_core.console.console import Console
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.live_server.live_server_thread import LiveServerThread
from cpl_core.utils import String
class LiveServerService(FileSystemEventHandler):
def __init__(
self,
env: ApplicationEnvironmentABC,
project_settings: ProjectSettings,
build_settings: BuildSettings,
publisher: PublisherService,
):
"""
Service for the live development server
:param env:
:param project_settings:
:param build_settings:
"""
FileSystemEventHandler.__init__(self)
self._env = env
self._project_settings = project_settings
self._build_settings = build_settings
self._publisher = publisher
self._src_dir = os.path.join(self._env.working_directory, self._build_settings.source_path)
self._wd = self._src_dir
self._ls_thread = None
self._observer = None
self._args: list[str] = []
self._is_dev = False
def _start_observer(self):
"""
Starts the file changes observer
:return:
"""
self._observer = Observer()
self._observer.schedule(self, path=os.path.abspath(os.path.join(self._src_dir, "../")), recursive=True)
self._observer.start()
def _restart(self):
"""
Restarts the CPL project
:return:
"""
for proc in psutil.process_iter():
with suppress(Exception):
if proc.cmdline() == self._ls_thread.command:
proc.kill()
Console.write_line("Restart\n")
while self._ls_thread.is_alive():
time.sleep(1)
self._start()
def on_modified(self, event):
"""
Triggers when source file is modified
:param event:
:return:
"""
if event.is_directory:
return None
# Event is modified, you can process it now
if str(event.src_path).endswith(".py"):
self._observer.stop()
self._restart()
def _start(self):
self._build()
self._start_observer()
self._ls_thread = LiveServerThread(
self._project_settings.python_executable, self._wd, self._args, self._env, self._build_settings
)
self._ls_thread.start()
self._ls_thread.join()
Console.close()
def _build(self):
if self._is_dev:
return
self._env.set_working_directory(self._src_dir)
self._publisher.build()
self._env.set_working_directory(self._src_dir)
self._wd = os.path.abspath(
os.path.join(
self._src_dir,
self._build_settings.output_path,
self._project_settings.name,
"build",
String.convert_to_snake_case(self._project_settings.name),
)
)
def start(self, args: list[str]):
"""
Starts the CPL live development server
:param args:
:return:
"""
if self._build_settings.main == "":
Console.error("Project has no entry point.")
return
if "dev" in args:
self._is_dev = True
args.remove("dev")
self._args = args
Console.write_line("** CPL live development server is running **")
self._start()

View File

@@ -1,86 +0,0 @@
import os
import subprocess
import sys
import threading
from datetime import datetime
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_cli.configuration import BuildSettings
class LiveServerThread(threading.Thread):
def __init__(
self, executable: str, path: str, args: list[str], env: ApplicationEnvironmentABC, build_settings: BuildSettings
):
"""
Thread to start the CPL project for the live development server
:param executable:
:param path:
:param args:
:param env:
:param build_settings:
"""
threading.Thread.__init__(self)
self._executable = os.path.abspath(executable)
self._path = path
self._args = args
self._env = env
self._build_settings = build_settings
self._main = ""
self._command = []
self._env_vars = os.environ
@property
def command(self) -> list[str]:
return self._command
@property
def main(self) -> str:
return self._main
def run(self):
"""
Starts the CPL project
:return:
"""
main = self._build_settings.main
if "." in self._build_settings.main:
length = len(self._build_settings.main.split(".")) - 1
main = self._build_settings.main.split(".")[length]
self._main = os.path.join(self._path, f"{main}.py")
if not os.path.isfile(self._main):
Console.error("Entry point main.py not found")
return
# set cwd to src/
self._env.set_working_directory(os.path.abspath(os.path.join(self._path)))
src_cwd = os.path.abspath(os.path.join(self._path, "../"))
if sys.platform == "win32":
self._env_vars["PYTHONPATH"] = (
f"{src_cwd};" f"{os.path.join(self._env.working_directory, self._build_settings.source_path)}"
)
else:
self._env_vars["PYTHONPATH"] = (
f"{src_cwd}:" f"{os.path.join(self._env.working_directory, self._build_settings.source_path)}"
)
Console.set_foreground_color(ForegroundColorEnum.green)
Console.write_line("Read successfully")
Console.set_foreground_color(ForegroundColorEnum.cyan)
now = datetime.now()
Console.write_line(f'Started at {now.strftime("%Y-%m-%d %H:%M:%S")}\n\n')
Console.set_foreground_color(ForegroundColorEnum.default)
self._command = [self._executable, self._main]
# if len(self._args) > 0:
# self._command.append(' '.join(self._args))
for arg in self._args:
self._command.append(arg)
subprocess.run(self._command, env=self._env_vars)

View File

@@ -1,84 +0,0 @@
import os
import subprocess
import sys
from datetime import datetime
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_cli.configuration.build_settings import BuildSettings
class StartExecutable:
def __init__(self, env: ApplicationEnvironmentABC, build_settings: BuildSettings):
"""
Service to start the CPL project for the live development server
:param env:
:param build_settings:
"""
self._executable = None
self._env = env
self._build_settings = build_settings
self._main = ""
self._command = []
self._env_vars = os.environ
self._set_venv()
def _set_venv(self):
if self._executable is None or self._executable == sys.executable:
return
path = os.path.abspath(os.path.dirname(os.path.dirname(self._executable)))
if sys.platform == "win32":
self._env_vars["PATH"] = f"{path}\\bin" + os.pathsep + os.environ.get("PATH", "")
else:
self._env_vars["PATH"] = f"{path}/bin" + os.pathsep + os.environ.get("PATH", "")
self._env_vars["VIRTUAL_ENV"] = path
def run(self, args: list[str], executable: str, path: str, output=True):
self._executable = os.path.abspath(os.path.join(self._env.working_directory, executable))
if not os.path.exists(self._executable):
Console.error(f"Executable not found")
return
main = self._build_settings.main
if "." in self._build_settings.main:
length = len(self._build_settings.main.split(".")) - 1
main = self._build_settings.main.split(".")[length]
self._main = os.path.join(path, f"{main}.py")
if not os.path.isfile(self._main):
Console.error("Entry point main.py not found")
return
# set cwd to src/
self._env.set_working_directory(os.path.abspath(os.path.join(path)))
src_cwd = os.path.abspath(os.path.join(path, "../"))
if sys.platform == "win32":
self._env_vars["PYTHONPATH"] = (
f"{src_cwd};" f"{os.path.join(self._env.working_directory, self._build_settings.source_path)}"
)
else:
self._env_vars["PYTHONPATH"] = (
f"{src_cwd}:" f"{os.path.join(self._env.working_directory, self._build_settings.source_path)}"
)
if output:
Console.set_foreground_color(ForegroundColorEnum.green)
Console.write_line("Read successfully")
Console.set_foreground_color(ForegroundColorEnum.cyan)
Console.write_line(f'Started at {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n\n')
Console.set_foreground_color(ForegroundColorEnum.default)
self._command = [self._executable, self._main]
# if len(self._args) > 0:
# self._command.append(' '.join(self._args))
for arg in args:
self._command.append(arg)
subprocess.run(self._command, env=self._env_vars)

View File

@@ -1,56 +0,0 @@
import importlib.metadata
from typing import Type
from cpl_cli.cli import CLI
from cpl_cli.startup import Startup
from cpl_cli.startup_argument_extension import StartupArgumentExtension
from cpl_cli.startup_migration_extension import StartupMigrationExtension
from cpl_cli.startup_workspace_extension import StartupWorkspaceExtension
from cpl_core.application.application_builder import ApplicationBuilder
from cpl_core.application.startup_extension_abc import StartupExtensionABC
from cpl_core.console import Console
def get_startup_extensions() -> list[Type[StartupExtensionABC]]:
blacklisted_packages = ["cpl-cli"]
startup_extensions = []
installed_packages = importlib.metadata.distributions()
for p in installed_packages:
if not p.name.startswith("cpl-") or p.name in blacklisted_packages:
continue
package = p.name.replace("-", "_")
loaded_package = __import__(package)
if "__cli_startup_extension__" not in dir(loaded_package):
continue
startup_extensions.append(loaded_package.__cli_startup_extension__)
return startup_extensions
def main():
app_builder = ApplicationBuilder(CLI)
app_builder.use_startup(Startup)
app_builder.use_extension(StartupWorkspaceExtension)
app_builder.use_extension(StartupArgumentExtension)
app_builder.use_extension(StartupMigrationExtension)
for extension in get_startup_extensions():
app_builder.use_extension(extension)
app_builder.build().run()
Console.write_line()
if __name__ == "__main__":
main()
# ((
# ( `)
# ; / ,
# / \/
# / |
# / ~/
# / ) ) ~ edraft
# ___// | /
# `--' \_~-,

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.migrations"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.migrations.base"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,15 +0,0 @@
from abc import ABC, abstractmethod
class MigrationABC(ABC):
@abstractmethod
def __init__(self, version: str):
self._version = version
@property
def version(self) -> str:
return self._version
@abstractmethod
def migrate(self):
pass

View File

@@ -1,11 +0,0 @@
from abc import ABC, abstractmethod
class MigrationServiceABC(ABC):
@abstractmethod
def __init__(self):
pass
@abstractmethod
def migrate_from(self, version: str):
pass

View File

@@ -1,10 +0,0 @@
from cpl_cli.migrations.base.migration_abc import MigrationABC
class Migration202210(MigrationABC):
def __init__(self):
MigrationABC.__init__(self, "2022.10")
def migrate(self):
# This migration could be deleted, but stays as an example.
pass

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.migrations.service"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,20 +0,0 @@
from packaging import version
from cpl_cli.migrations.base.migration_abc import MigrationABC
from cpl_cli.migrations.base.migration_service_abc import MigrationServiceABC
from cpl_core.dependency_injection import ServiceProviderABC
class MigrationService(MigrationServiceABC):
def __init__(self, services: ServiceProviderABC):
MigrationServiceABC.__init__(self)
self._services = services
def migrate_from(self, _v: str):
for migration_type in MigrationABC.__subclasses__():
migration: MigrationABC = self._services.get_service(migration_type)
if version.parse(migration.version) <= version.parse(_v):
continue
migration.migrate()

View File

@@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.publish"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
from .publisher_abc import PublisherABC
from .publisher_service import PublisherService
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,33 +0,0 @@
from abc import abstractmethod, ABC
class PublisherABC(ABC):
@abstractmethod
def __init__(self):
ABC.__init__(self)
@property
@abstractmethod
def source_path(self) -> str:
pass
@property
@abstractmethod
def dist_path(self) -> str:
pass
@abstractmethod
def include(self, path: str):
pass
@abstractmethod
def exclude(self, path: str):
pass
@abstractmethod
def build(self):
pass
@abstractmethod
def publish(self):
pass

View File

@@ -1,519 +0,0 @@
import os
import shutil
import sys
from string import Template as stringTemplate
import setuptools
from packaging import version
from setuptools import sandbox
from cpl_core.configuration.configuration_abc import ConfigurationABC
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from cpl_core.console.console import Console
from cpl_core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.publish.publisher_abc import PublisherABC
from cpl_cli._templates.build.init_template import InitTemplate
from cpl_cli._templates.publish.setup_template import SetupTemplate
class PublisherService(PublisherABC):
def __init__(
self, config: ConfigurationABC, env: ApplicationEnvironmentABC, project: ProjectSettings, build: BuildSettings
):
"""
Service to build or publish files for distribution
:param config:
:param env:
:param project:
:param build:
"""
PublisherABC.__init__(self)
self._config = config
self._env = env
self._project_settings = project
self._build_settings = build
self._source_path = os.path.join(self._env.working_directory, self._build_settings.source_path)
self._output_path = os.path.join(self._env.working_directory, self._build_settings.output_path)
self._included_files: list[str] = []
self._included_dirs: list[str] = []
self._distributed_files: list[str] = []
self._path_mark = "/"
if sys.platform == "win32":
self._path_mark = "\\"
self._src_path_part = f"src{self._path_mark}"
@property
def source_path(self) -> str:
return self._source_path
@property
def dist_path(self) -> str:
return self._output_path
def _get_module_name_from_dirs(self, file: str) -> str:
"""
Extracts module name from directories
:param file:
:return:
"""
if self._src_path_part in file:
file = file.split(self._src_path_part)[1].replace(self._src_path_part, "", 1)
dirs = os.path.dirname(file).split(self._path_mark)
for d in dirs:
if d.__contains__("."):
dirs.remove(d)
if len(dirs) == 0:
return os.path.basename(file)
else:
return ".".join(dirs)
@staticmethod
def _delete_path(path: str):
"""
Deletes full path tree
:param path:
:return:
"""
if os.path.isdir(path):
try:
shutil.rmtree(path)
except Exception as e:
Console.error(f"{e}")
sys.exit()
@staticmethod
def _create_path(path: str):
"""
Creates full path tree
:param path:
:return:
"""
if not os.path.isdir(path):
try:
os.makedirs(path)
except Exception as e:
Console.error(f"{e}")
sys.exit()
def _is_path_included(self, path: str) -> bool:
"""
Checks if the path is included
:param path:
:return:
"""
for included in self._build_settings.included:
if included.startswith("*"):
included = included.replace("*", "")
if included in path and path not in self._build_settings.excluded:
return True
return False
def _is_path_excluded(self, path: str) -> bool:
"""
Checks if the path is excluded
:param path:
:return:
"""
for excluded in self._build_settings.excluded:
if excluded.startswith("*"):
excluded = excluded.replace("*", "")
if excluded in path and not self._is_path_included(path):
return True
return False
def _is_file_excluded(self, file: str) -> bool:
"""
Checks if the file is excluded
:param file:
:return:
"""
for excluded in self._build_settings.excluded:
if excluded.startswith("*"):
excluded = excluded.replace("*", "")
if excluded in file and not self._is_path_included(file):
return True
return False
def _read_sources_from_path(self, path: str):
"""
Reads all source files from given path
:param path:
:return:
"""
for r, d, f in os.walk(path):
for file in f:
relative_path = os.path.relpath(r)
file_path = os.path.join(relative_path, os.path.relpath(file))
if self._is_file_excluded(file_path):
continue
if len(d) > 0:
for directory in d:
empty_dir = os.path.join(os.path.dirname(file_path), directory)
if len(os.listdir(empty_dir)) == 0:
self._included_dirs.append(empty_dir)
if not self._is_path_excluded(relative_path):
self._included_files.append(os.path.relpath(file_path))
def _read_sources(self):
"""
Reads all source files and save included files
:return:
"""
for file in self._build_settings.included:
rel_path = os.path.relpath(file)
if os.path.isdir(rel_path):
for r, d, f in os.walk(rel_path):
for sub_file in f:
relative_path = os.path.relpath(r)
file_path = os.path.join(relative_path, os.path.relpath(sub_file))
self._included_files.append(os.path.relpath(file_path))
elif os.path.isfile(rel_path):
self._included_files.append(rel_path)
self._read_sources_from_path(self._source_path)
for project in self._build_settings.project_references:
project = os.path.abspath(os.path.join(self._source_path, project))
if not os.path.isfile(os.path.abspath(project)):
Console.error(f"Cannot import project: {project}")
return
self.exclude(f"*/{os.path.basename(project)}")
self._read_sources_from_path(os.path.dirname(project))
def _create_packages(self):
"""
Writes information from template to all included __init__.py
:return:
"""
for file in self._included_files:
if not file.endswith("__init__.py"):
continue
template_content = ""
module_file_lines: list[str] = []
title = self._get_module_name_from_dirs(file)
if title == "":
title = self._project_settings.name
module_py_lines: list[str] = []
imports = ""
with open(file, "r") as py_file:
module_file_lines = py_file.readlines()
py_file.close()
if len(module_file_lines) == 0:
imports = "# imports:"
else:
is_started = False
build_ignore = False
for line in module_file_lines:
if line.__contains__("# imports"):
is_started = True
if line.__contains__("# build-ignore"):
build_ignore = True
if line.__contains__("# build-ignore-end") and is_started:
module_py_lines.append("# build-ignore-end".replace("\n", ""))
build_ignore = False
if (
((line.__contains__("from") or line.__contains__("import")) and is_started)
or line.startswith("__cli_startup_extension__")
or build_ignore
):
module_py_lines.append(line.replace("\n", ""))
if len(module_py_lines) > 0:
imports = "\n".join(module_py_lines)
template_content = stringTemplate(InitTemplate.get_init_py()).substitute(
Name=self._project_settings.name,
Description=self._project_settings.description,
LongDescription=self._project_settings.long_description,
CopyrightDate=self._project_settings.copyright_date,
CopyrightName=self._project_settings.copyright_name,
LicenseName=self._project_settings.license_name,
LicenseDescription=self._project_settings.license_description,
Title=title if title is not None and title != "" else self._project_settings.name,
Author=self._project_settings.author,
Version=version.parse(self._project_settings.version.to_str()),
Major=self._project_settings.version.major,
Minor=self._project_settings.version.minor,
Micro=self._project_settings.version.micro,
Imports=imports,
)
with open(file, "w+") as py_file:
py_file.write(template_content)
py_file.close()
def _dist_files(self):
"""
Copies all included source files to dist_path
:return:
"""
build_path = os.path.join(self._output_path)
self._delete_path(build_path)
self._create_path(build_path)
for file in self._included_files:
dist_file = file
if self._src_path_part in dist_file:
dist_file = dist_file.replace(self._src_path_part, "", 1)
output_path = os.path.join(build_path, os.path.dirname(dist_file))
output_file = os.path.join(build_path, dist_file)
try:
if not os.path.isdir(output_path):
os.makedirs(output_path, exist_ok=True)
except Exception as e:
Console.error(__name__, f"Cannot create directories: {output_path} -> {e}")
return
try:
self._distributed_files.append(output_file)
shutil.copy(os.path.abspath(file), output_file)
except Exception as e:
Console.error(__name__, f"Cannot copy file: {file} to {output_path} -> {e}")
return
for empty_dir in self._included_dirs:
dist_dir = empty_dir
if self._src_path_part in dist_dir:
dist_dir = dist_dir.replace(self._src_path_part, "", 1)
output_path = os.path.join(build_path, dist_dir)
if not os.path.isdir(output_path):
os.makedirs(output_path)
def _clean_dist_files(self):
"""
Deletes all included source files from dist_path
:return:
"""
paths: list[str] = []
for file in self._distributed_files:
paths.append(os.path.dirname(file))
if os.path.isfile(file):
os.remove(file)
for path in paths:
if path != self._output_path and os.path.isdir(path):
shutil.rmtree(path)
def _create_setup(self):
"""
Generates setup.py
Dependencies: ProjectSettings, BuildSettings
:return:
"""
setup_file = os.path.join(self._output_path, "setup.py")
if os.path.isfile(setup_file):
os.remove(setup_file)
entry_points = {}
if self._build_settings.main != "":
main = None
try:
main_name = self._build_settings.main
if "." in self._build_settings.main:
length = len(self._build_settings.main.split("."))
main_name = self._build_settings.main.split(".")[length - 1]
sys.path.insert(0, os.path.join(self._source_path, "../"))
main_mod = __import__(self._build_settings.main)
main = getattr(main_mod, main_name)
except Exception as e:
Console.error("Could not find entry point", str(e))
return
if main is None or not callable(main) and not hasattr(main, "main"):
Console.error("Could not find entry point")
return
if callable(main):
mod_name = main.__module__
func_name = main.__name__
else:
mod_name = main.__name__
func_name = main.main.__name__
entry_points = {"console_scripts": [f"{self._build_settings.entry_point} = {mod_name}:{func_name}"]}
with open(setup_file, "w+") as setup_py:
setup_string = stringTemplate(SetupTemplate.get_setup_py()).substitute(
Name=self._project_settings.name,
Version=self._project_settings.version.to_str(),
Packages=setuptools.find_packages(where=self._output_path, exclude=self._build_settings.excluded),
URL=self._project_settings.url,
LicenseName=self._project_settings.license_name,
Author=self._project_settings.author,
AuthorMail=self._project_settings.author_email,
IncludePackageData=self._build_settings.include_package_data,
Description=self._project_settings.description,
PyRequires=self._project_settings.python_version,
Dependencies=self._project_settings.dependencies,
EntryPoints=entry_points,
PackageData=self._build_settings.package_data,
)
setup_py.write(setup_string)
setup_py.close()
def _run_setup(self):
"""
Starts setup.py
:return:
"""
setup_py = os.path.join(self._output_path, "setup.py")
if not os.path.isfile(setup_py):
Console.error(__name__, f"setup.py not found in {self._output_path}")
return
try:
sandbox.run_setup(
os.path.abspath(setup_py),
[
"sdist",
f'--dist-dir={os.path.join(self._output_path, "setup")}',
"bdist_wheel",
f'--bdist-dir={os.path.join(self._output_path, "bdist")}',
f'--dist-dir={os.path.join(self._output_path, "setup")}',
],
)
os.remove(setup_py)
except Exception as e:
Console.error("Executing setup.py failed", str(e))
def include(self, path: str):
"""
Includes given path from sources
:param path:
:return:
"""
self._build_settings.included.append(path)
def exclude(self, path: str):
"""
Excludes given path from sources
:param path:
:return:
"""
self._build_settings.excluded.append(path)
def build(self):
"""
Build the CPL project to dist_path/build
1. Reads all included source files
2. Writes informations from template to all included __init__.py
3. Copies all included source files to dist_path/build
:return:
"""
self._env.set_working_directory(
os.path.join(self._env.working_directory, "../")
) # probably causing some errors (#125)
self.exclude(f"*/{self._project_settings.name}.json")
self._output_path = os.path.abspath(os.path.join(self._output_path, self._project_settings.name, "build"))
Console.spinner(
"Reading source files:",
self._read_sources,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.blue,
)
Console.spinner(
"Creating internal packages:",
self._create_packages,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.blue,
)
Console.spinner(
"Building application:",
self._dist_files,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.blue,
)
Console.write_line()
def publish(self):
"""
Publishes the CPL project to dist_path/publish
1. Builds the project
2. Generates setup.py
3. Start setup.py
4. Remove all included source from dist_path/publish
:return:
"""
self._env.set_working_directory(
os.path.join(self._env.working_directory, "../")
) # probably causing some errors (#125)
self.exclude(f"*/{self._project_settings.name}.json")
self._output_path = os.path.abspath(os.path.join(self._output_path, self._project_settings.name, "publish"))
Console.write_line("Build:")
Console.spinner(
"Reading source files:",
self._read_sources,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.blue,
)
Console.spinner(
"Creating internal packages:",
self._create_packages,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.blue,
)
Console.spinner(
"Building application:",
self._dist_files,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.blue,
)
Console.write_line("\nPublish:")
Console.spinner(
"Generating setup.py:",
self._create_setup,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.blue,
)
Console.write_line("Running setup.py:\n")
self._run_setup()
Console.spinner(
"Cleaning dist path:",
self._clean_dist_files,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.blue,
)
Console.write_line()

View File

@@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
cpl-cli CPL CLI
~~~~~~~~~~~~~~~~~~~
CPL Command Line Interface
:copyright: (c) 2020 - 2024 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = "cpl_cli.source_creator"
__author__ = "Sven Heidemann"
__license__ = "MIT"
__copyright__ = "Copyright (c) 2020 - 2024 sh-edraft.de"
__version__ = "2024.10.0"
from collections import namedtuple
# imports:
VersionInfo = namedtuple("VersionInfo", "major minor micro")
version_info = VersionInfo(major="2024", minor="10", micro="0")

View File

@@ -1,55 +0,0 @@
import json
import os
from cpl_cli.abc.file_template_abc import FileTemplateABC
from cpl_cli.configuration import WorkspaceSettings, WorkspaceSettingsNameEnum
from cpl_core.console import Console, ForegroundColorEnum
class TemplateBuilder:
@staticmethod
def build_cpl_file(file_name: str, content: dict):
if not os.path.isabs(file_name):
file_name = os.path.abspath(file_name)
path = os.path.dirname(file_name)
if not os.path.isdir(path):
os.makedirs(path)
with open(file_name, "w") as project_json:
project_json.write(json.dumps(content, indent=2))
project_json.close()
@classmethod
def create_workspace(cls, path: str, project_name, projects: dict, scripts: dict):
ws_dict = {
WorkspaceSettings.__name__: {
WorkspaceSettingsNameEnum.default_project.value: project_name,
WorkspaceSettingsNameEnum.projects.value: projects,
WorkspaceSettingsNameEnum.scripts.value: scripts,
}
}
Console.spinner(
f"Creating {path}",
cls.build_cpl_file,
path,
ws_dict,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan,
)
@staticmethod
def build(file_path: str, template: FileTemplateABC):
"""
Creates template
:param file_path:
:param template:
:return:
"""
if not os.path.isdir(os.path.dirname(file_path)):
os.makedirs(os.path.dirname(file_path))
with open(file_path, "w") as file:
file.write(template.value)
file.close()

Some files were not shown because too many files have changed in this diff Show More