136 Commits

Author SHA1 Message Date
80eb1da0e1 Merge pull request '2021.4.7' (#29) from 2021.4.7 into 2021.4.post1
Reviewed-on: #29
2021-04-13 15:47:24 +02:00
ae39d43ea9 Added useful deployment scripts & closed #22 2021-04-13 15:43:05 +02:00
602c48bc06 Bugfixes for help command handling 2021-04-13 15:27:19 +02:00
d34d501520 Bugfixes for help command 2021-04-12 20:22:28 +02:00
8f3ee53915 Fixed typos 2021-04-12 20:05:55 +02:00
7eca382cb3 Added help messages to cli commands 2021-04-12 20:02:45 +02:00
f8f5d46491 Added help message to generate command 2021-04-12 19:44:29 +02:00
696c62292b Added logic to cli to use --help option for help command 2021-04-12 19:43:55 +02:00
d94b25d9d8 Improved configuration console argument handling 2021-04-12 19:43:30 +02:00
93829027af Added help message to all cli commands 2021-04-11 16:36:59 +02:00
cce4b6b191 Merge pull request '2021.4.6' (#25) from 2021.4.6 into 2021.4.post1
Reviewed-on: #25
2021-04-11 15:54:37 +02:00
0876f06282 Improved cpl-project related files & closed #20 2021-04-11 15:50:13 +02:00
c970412a66 Fixed relative path handling in add command 2021-04-11 15:42:34 +02:00
6af9328e14 Added cli command add 2021-04-11 15:36:04 +02:00
5987b71c80 Improved configuration to allow multiple arguments 2021-04-11 15:09:11 +02:00
79fdc0c60b Improved configuration to allow multiple arguments 2021-04-11 15:00:54 +02:00
cfb6a84799 Fixed console argument handling 2021-04-11 14:44:46 +02:00
faa7e701fd Fixed workspace loading error for update command 2021-04-11 14:21:59 +02:00
d5b8286f68 Fixed workspace loading error for update command 2021-04-11 13:23:02 +02:00
a3d5737eaf Fixed workspace loading error for update command 2021-04-11 13:20:32 +02:00
314bb1b722 Fixed workspace loading error for update command 2021-04-11 13:15:19 +02:00
1c353bbcdf Improved cli workspace handling 2021-04-11 13:00:26 +02:00
036b9dcc2d Added more functions to string 2021-04-11 13:00:05 +02:00
0fec5719bc Added remove to help command 2021-04-11 12:34:02 +02:00
3572081e43 Added remove command 2021-04-11 12:33:05 +02:00
8cf7f494b3 Improved configuration 2021-04-11 12:18:36 +02:00
b1165e169a Improved console argument value handling in configuration 2021-04-11 11:42:48 +02:00
284ebe1b11 Added logic to cpl cli to change cwd only when wanted 2021-04-11 11:12:39 +02:00
d51659db3e Improved cli build and publish to use project references 2021-04-11 11:01:57 +02:00
3aa3a874c9 Improved default cwd in ApplicationEnvironment 2021-04-11 10:04:55 +02:00
113d3199b1 Improved main.py templates for new command to handle local imports 2021-04-10 12:04:47 +02:00
5712a9ad67 Improved file creation new command 2021-04-10 11:50:35 +02:00
1f8d420749 Improved workspace handling in new command 2021-04-10 11:46:36 +02:00
9065ef901c Improved new command to handle existing projects better 2021-04-10 11:24:39 +02:00
b2c5d304f8 Improved path and name handling in new command 2021-04-10 11:14:37 +02:00
70ae7dd9e6 Fixed workspace creation error 2021-04-10 11:06:58 +02:00
94b122d677 Removed unused import 2021-04-10 11:05:31 +02:00
f6fc5d09e7 Improved path and name handling in new command 2021-04-10 11:01:56 +02:00
2b5c75cc67 Improved workspace handling to new command for console type 2021-04-09 22:41:04 +02:00
167f813bcf Removed dev outputs 2021-04-09 22:26:55 +02:00
7b9765e5b1 Improved readme 2021-04-09 22:16:43 +02:00
7ad01d4bdd Improved new command to use workspace when found 2021-04-09 22:09:55 +02:00
d19c47e571 Added cpl-env to gitignore 2021-04-09 20:52:50 +02:00
4bfc647b91 Improved live dev server to use venv 2021-04-09 20:42:15 +02:00
98117ed7ad Moved templates/[..]/src to templates/[..]/source to prevent 'src' exclusion 2021-04-09 20:26:31 +02:00
cc9b25356c Improved workspace and venv handling for uninstall and install command 2021-04-09 20:07:38 +02:00
a95d6e7f8a Improved configuration to handle absolute paths in add_json_file 2021-04-08 21:40:50 +02:00
fe5c41ac09 Added workspace handling to update command 2021-04-08 21:18:29 +02:00
b672174960 Added workspace handling to uninstall command 2021-04-08 21:14:38 +02:00
a8691ced5b Added workspace handling to install command 2021-04-08 21:12:00 +02:00
fc53f90ecd Added workspace handling for new command 2021-04-08 20:58:55 +02:00
0f836b5c7d Published after file move 2021-04-08 20:28:34 +02:00
34ed7123d1 Improved workspace handling in publish and build command 2021-04-08 20:28:19 +02:00
5f668e17c9 Improved workspace handling in start command 2021-04-08 19:30:00 +02:00
806888bf49 Removed content_root_path 2021-04-08 17:50:54 +02:00
e8756a09ca Improved general test project file 2021-04-08 16:46:46 +02:00
e38988be1b Changed project files of general test project 2021-04-07 22:11:22 +02:00
4009c93353 Added workspace handling to live dev server 2021-04-07 22:00:56 +02:00
3ba5c10fad Publish CLI 2021.4 2021-04-07 19:19:20 +02:00
2552f1d43c Added workspace handling to publish command 2021-04-07 19:18:55 +02:00
5be2e83798 Improved configuration 2021-04-07 18:57:00 +02:00
d4919e027c Removed console output 2021-04-07 18:37:57 +02:00
7ef238b64d Added workspace handling to build command 2021-04-07 18:36:08 +02:00
703d20f43f Improved version handling 2021-04-07 18:35:14 +02:00
d9bd7911ef Improved start command to use workspace 2021-04-07 18:19:55 +02:00
6a5c832288 Added new file structure 2021-04-07 18:19:34 +02:00
dc849c4f71 Improved application environment 2021-04-07 18:06:07 +02:00
76a6ba11fb Publish 2021.4.post1 for local dev 2021-04-07 17:01:39 +02:00
305a19730f Merge pull request 'Fixed #21 - Added logic to automatically add 'src' to path in generate command' (#24) from 2021.4.5 into 2021.4.post1
Reviewed-on: #24
2021-04-07 16:59:24 +02:00
de15e81703 Fixed #21 - Added logic to automatically add 'src' to path in generate command 2021-04-07 16:55:44 +02:00
b91544a0da Edited LICENSE 2021-04-01 11:09:50 +02:00
caca34168e Merge pull request '2021.4' (#19) from 2021.4 into master
Reviewed-on: sh-edraft.de/sh_common_py_lib#19
2021-04-01 10:13:32 +02:00
18b8ac589d Publish 2021.4.0 2021-04-01 10:04:13 +02:00
d85e628784 Improved readme 2021-03-31 15:51:14 +02:00
1e150cf10c Improved readme 2021-03-31 15:49:52 +02:00
9b0f0b7559 Improved readme 2021-03-31 15:49:00 +02:00
cc118b5030 Merge pull request '2021.04.03 - Readme' (#18) from 2021.04.03 into 2021.4
Reviewed-on: sh-edraft.de/sh_common_py_lib#18
2021-03-31 15:42:40 +02:00
3e7ff78861 Merge branch '2021.4' into 2021.04.03 2021-03-31 15:40:18 +02:00
448d538d21 Updated reame 2021-03-31 15:38:26 +02:00
d368161eb3 Publish 2021.4.0rc7 2021-03-31 12:11:55 +02:00
eef8488ca2 Bugfixes for new lib 2021-03-31 12:11:19 +02:00
b22d04b145 Publish 2021.4.0rc6 2021-03-31 12:03:15 +02:00
6579e54c2b Improved windows support 2021-03-31 12:02:51 +02:00
88c0153ca2 Improved windows support for publisher 2021-03-31 11:55:46 +02:00
f28c6ab309 Removed prints 2021-03-31 11:51:20 +02:00
976041f812 Publish 2021.4.0rc5 2021-03-31 11:48:26 +02:00
7693dd462f Bugfixes for publisher windows support 2021-03-31 11:47:47 +02:00
df5e61cf98 Bugfixes for publisher windows support 2021-03-31 11:47:28 +02:00
df87d08be3 Bugfix for live server windows support 2021-03-31 11:41:16 +02:00
33cb35e26f Publish 2021.4.0rc4.post1 2021-03-31 11:25:07 +02:00
18b2cd8f2f Publish 2021.4.0rc4 2021-03-31 11:01:08 +02:00
7ab2a881e8 Bugfixes 2021-03-31 10:47:14 +02:00
960de880ee Bugfixes 2021-03-31 10:44:54 +02:00
c0d8904782 Bugfixes for the command new 2021-03-31 10:19:54 +02:00
b6d508526a Bugfixes for the command new 2021-03-31 09:46:41 +02:00
31307485f0 Bugfixes in new command template 2021-03-31 09:13:04 +02:00
612c3583f1 Removed ununsed files 2021-03-30 21:02:36 +02:00
6cdd3376f1 Publish 2021.4.0rc3 2021-03-30 21:01:10 +02:00
b947789779 Bugfixes 2021-03-30 20:59:09 +02:00
0da83935fc Bugfixes 2021-03-30 12:44:31 +02:00
3428c70b69 Bugfixes 2021-03-30 11:54:44 +02:00
3d511ff727 Bugfixes 2021-03-30 11:53:07 +02:00
cf2f293290 Publish 2021.4.0rc2 2021-03-30 11:03:16 +02:00
c39601a35d Improved publisher 2021-03-30 11:02:58 +02:00
5f36e0a43e Improved publisher 2021-03-30 10:43:39 +02:00
31fe1c5cd7 Improved service collection 2021-03-30 10:29:50 +02:00
0cdecd893a Improved database context 2021-03-30 10:06:07 +02:00
260fe06be8 Improved new command 2021-03-30 09:54:04 +02:00
8ddfd35a82 Improved generate command 2021-03-30 09:47:47 +02:00
099bae3d7c Removed usage of ServiceABC 2021-03-30 09:27:45 +02:00
9758e1fae2 Improved update command 2021-03-29 16:16:27 +02:00
211143d6ab Package update 2021-03-29 16:06:54 +02:00
70b49a098a Publish 2021.4.0rc1 2021-03-29 15:59:05 +02:00
e2544f90fe Merge pull request '2021.4.2' (#12) from 2021.4.2 into 2021.4
Reviewed-on: sh-edraft.de/sh_common_py_lib#12
2021-03-29 15:48:58 +02:00
c223524d1b Removed direct import 2021-03-29 15:47:41 +02:00
1789e2671a Publish 2021.4.2 2021-03-29 15:26:45 +02:00
d0812bcf5c Added database table test 2021-03-29 14:29:17 +02:00
4d5368e5fc Bugfixes & improved database context handling 2021-03-29 14:18:58 +02:00
f3be927440 Removed service abc 2021-03-29 13:29:26 +02:00
d0e3e1792f Added db context handling 2021-03-29 13:25:25 +02:00
31793fb290 Added db context handling 2021-03-29 13:25:15 +02:00
41ddc86961 Bugfixes 2021-03-29 12:16:12 +02:00
30ef74dd18 Added generated simple di test app 2021-03-29 11:48:25 +02:00
bc69476bb2 Improved new project templates & publish 2021.4.2.dev1 2021-03-29 11:47:49 +02:00
05b9fd801b Bugfixes 2021-03-29 09:21:58 +02:00
7af7f866c1 Removed application runtime | Code refactoring p.2 2021-03-29 08:56:18 +02:00
069e44bee6 Code refactoring p.1 2021-03-27 08:41:33 +01:00
3c0dd00a99 Bugfixes 2021-03-26 13:16:40 +01:00
afb91c8f1f Improved service provider 2021-03-23 21:42:08 +01:00
ca51f88d2b Improved service provider 2021-03-23 21:39:29 +01:00
a683b70c5f [WIP] Improved service collection 2021-03-23 20:23:30 +01:00
c650e87443 [WIP] Added first changes for the DI 2021-03-23 20:21:24 +01:00
36dfedf918 Bugfixes cpl cli output 2021-03-23 17:31:24 +01:00
252f2b55fe Bugfixes in templates 2021-03-23 17:29:13 +01:00
99091a2540 Publish for 2021.4.2 2021-03-21 20:22:04 +01:00
c3fdb7a049 Changed version 2021-03-21 20:05:51 +01:00
171 changed files with 3614 additions and 943 deletions

3
.gitignore vendored
View File

@@ -110,6 +110,9 @@ ENV/
env.bak/ env.bak/
venv.bak/ venv.bak/
# Custom Environments
cpl-env/
# Spyder project settings # Spyder project settings
.spyderproject .spyderproject
.spyproject .spyproject

View File

@@ -1,4 +1,4 @@
MIT License Copyright (c) <year> <copyright holders> MIT License Copyright (c) 2020 - 2021 sh-edraft.de
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

106
README.md
View File

@@ -1,2 +1,106 @@
# sh_common_py_lib <h1 align="center">CPL - Common python library</h1>
<!-- Summary -->
<p align="center">
<!-- <img src="" alt="cpl-logo" width="120px" height="120px"/> -->
<br>
<i>
CPL is a development platform for python server applications
<br>using Python.</i>
<br>
</p>
## Table of Contents
<!-- TABLE OF CONTENTS -->
<ol>
<li>
<a href="#getting-started">Getting Started</a>
<ul>
<li><a href="#prerequisites">Prerequisites</a></li>
<li><a href="#installation">Installation</a></li>
</ul>
</li>
<li><a href="#roadmap">Roadmap</a></li>
<li><a href="#contributing">Contributing</a></li>
<li><a href="#license">License</a></li>
<li><a href="#contact">Contact</a></li>
</ol>
<!-- GETTING STARTED -->
## Getting Started
[Get started with CPL][quickstart].
### Prerequisites
- Install [python] which includes [Pip installs packages][pip]
### Installation
Install the CPL package
```sh
pip install sh_cpl --extra-index-url https://pip.sh-edraft.de
```
Install the CPL CLI
```sh
pip install sh_cpl-cli --extra-index-url https://pip.sh-edraft.de
```
Create workspace:
```sh
cpl new <console|library> <PROJECT NAME>
```
Run the application:
```sh
cd <PROJECT NAME>
cpl start
```
<!-- ROADMAP -->
## Roadmap
See the [open issues](https://git.sh-edraft.de/sh-edraft.de/sh_common_py_lib/issues) for a list of proposed features (and known issues).
<!-- CONTRIBUTING -->
## Contributing
### Contributing Guidelines
Read through our [contributing guidelines][contributing] to learn about our submission process, coding rules and more.
### Want to Help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for [contributing][contributing].
<!-- LICENSE -->
## License
Distributed under the MIT License. See [LICENSE] for more information.
<!-- CONTACT -->
## Contact
Sven Heidemann - sven.heidemann@sh-edraft.de
Project link: [https://git.sh-edraft.de/sh-edraft.de/sh_common_py_lib](https://git.sh-edraft.de/sh-edraft.de/sh_common_py_lib)
<!-- External LINKS -->
[pip_url]: https://pip.sh-edraft.de
[python]: https://www.python.org/
[pip]: https://pypi.org/project/pip/
<!-- Internal LINKS -->
[project]: https://git.sh-edraft.de/sh-edraft.de/sh_common_py_lib
[quickstart]: https://git.sh-edraft.de/sh-edraft.de/sh_common_py_lib/wiki/quickstart
[contributing]: https://git.sh-edraft.de/sh-edraft.de/sh_common_py_lib/wiki/contributing
[license]: LICENSE

9
cpl-workspace.json Normal file
View File

@@ -0,0 +1,9 @@
{
"WorkspaceSettings": {
"DefaultProject": "cpl_cli",
"Projects": {
"cpl": "src/cpl/cpl.json",
"cpl_cli": "src/cpl_cli/cpl_cli.json"
}
}
}

View File

@@ -1,19 +0,0 @@
prefix: cpl
commands:
build
generate:
abc | a
class | c
configmodel | cm
enum | e
service | s
help
new
console
start
publish
update
version

View File

@@ -1,2 +0,0 @@
python setup.py install # for install
python setup.py sdist bdist_wheel # for build

View File

@@ -1,11 +1,39 @@
upload: upload:
prod: prod:
twine upload --repository-url https://pip.sh-edraft.de dist/publish/setup/* cpl:
twine upload -r pip.sh-edraft.de dist/publish/setup/* twine upload --repository-url https://pip.sh-edraft.de dist/sh_cpl/publish/setup/*
twine upload -r pip.sh-edraft.de dist/sh_cpl-cli/publish/setup/*
cli:
twine upload --repository-url https://pip.sh-edraft.de dist/sh_cpl-cli/publish/setup/*
twine upload -r pip.sh-edraft.de dist/sh_cpl-cli/publish/setup/*
exp:
cpl:
twine upload --repository-url https://pip-exp.sh-edraft.de dist/sh_cpl/publish/setup/*
twine upload -r pip-exp.sh-edraft.de dist/sh_cpl/publish/setup/*
cli:
twine upload --repository-url https://pip-exp.sh-edraft.de dist/sh_cpl-cli/publish/setup/*
twine upload -r pip-exp.sh-edraft.de dist/sh_cpl-cli/publish/setup/*
dev: dev:
twine upload --repository-url https://pip-dev.sh-edraft.de dist/publish/setup/* cpl:
twine upload -r pip-dev.sh-edraft.de dist/publish/setup/* twine upload --repository-url https://pip-dev.sh-edraft.de dist/sh_cpl/publish/setup/*
twine upload -r pip-dev.sh-edraft.de dist/sh_cpl/publish/setup/*
cli:
twine upload --repository-url https://pip-dev.sh-edraft.de dist/sh_cpl-cli/publish/setup/*
twine upload -r pip-dev.sh-edraft.de dist/sh_cpl-cli/publish/setup/*
install: install:
pip install --extra-index-url https://pip.sh-edraft.de/ sh_cpl prod:
pip install --extra-index-url https://pip.sh-edraft.de/ sh_cpl
pip install --extra-index-url https://pip.sh-edraft.de/ sh_cpl-cli
exp:
pip install --extra-index-url https://pip-exp.sh-edraft.de/ sh_cpl
pip install --extra-index-url https://pip-exp.sh-edraft.de/ sh_cpl-cli
dev:
pip install --extra-index-url https://pip-dev.sh-edraft.de/ sh_cpl
pip install --extra-index-url https://pip-dev.sh-edraft.de/ sh_cpl-cli

11
scripts/build.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
# activate venv
source /home/sven/Nextcloud_Sven/Schreibtisch/git_sh-edraft_de/sh_cpl/cpl-env/bin/activate
# CLI
cd /home/sven/Nextcloud_Sven/Schreibtisch/git_sh-edraft_de/sh_cpl/
cpl build
# CPL
cd /home/sven/Nextcloud_Sven/Schreibtisch/git_sh-edraft_de/sh_cpl/src/cpl
cpl build

11
scripts/publish.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
# activate venv
source /home/sven/Nextcloud_Sven/Schreibtisch/git_sh-edraft_de/sh_cpl/cpl-env/bin/activate
# CLI
cd /home/sven/Nextcloud_Sven/Schreibtisch/git_sh-edraft_de/sh_cpl/
cpl publish
# CPL
cd /home/sven/Nextcloud_Sven/Schreibtisch/git_sh-edraft_de/sh_cpl/src/cpl
cpl publish

View File

@@ -11,15 +11,15 @@ sh-edraft Common Python library
""" """
__title__ = 'sh_cpl.cpl' __title__ = 'cpl'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.application'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -23,9 +23,7 @@ from collections import namedtuple
from .application_abc import ApplicationABC from .application_abc import ApplicationABC
from .application_builder import ApplicationBuilder from .application_builder import ApplicationBuilder
from .application_builder_abc import ApplicationBuilderABC from .application_builder_abc import ApplicationBuilderABC
from .application_runtime import ApplicationRuntime
from .application_runtime_abc import ApplicationRuntimeABC
from .startup_abc import StartupABC from .startup_abc import StartupABC
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -1,21 +1,21 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Optional from typing import Optional
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
from cpl.configuration.configuration_abc import ConfigurationABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.console import Console from cpl.console.console import Console
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
from cpl.environment import ApplicationEnvironmentABC
class ApplicationABC(ABC): class ApplicationABC(ABC):
@abstractmethod @abstractmethod
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC, services: ServiceProviderABC): def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
""" """
ABC of application ABC of application
""" """
self._configuration: Optional[ConfigurationABC] = config self._configuration: Optional[ConfigurationABC] = config
self._runtime: Optional[ApplicationRuntimeABC] = runtime self._environment: Optional[ApplicationEnvironmentABC] = self._configuration.environment
self._services: Optional[ServiceProviderABC] = services self._services: Optional[ServiceProviderABC] = services
def run(self): def run(self):

View File

@@ -2,10 +2,9 @@ from typing import Type, Optional
from cpl.application.application_abc import ApplicationABC from cpl.application.application_abc import ApplicationABC
from cpl.application.application_builder_abc import ApplicationBuilderABC from cpl.application.application_builder_abc import ApplicationBuilderABC
from cpl.application.application_runtime import ApplicationRuntime
from cpl.application.startup_abc import StartupABC from cpl.application.startup_abc import StartupABC
from cpl.configuration import Configuration from cpl.configuration.configuration import Configuration
from cpl.dependency_injection import ServiceProvider from cpl.dependency_injection.service_collection import ServiceCollection
class ApplicationBuilder(ApplicationBuilderABC): class ApplicationBuilder(ApplicationBuilderABC):
@@ -19,8 +18,8 @@ class ApplicationBuilder(ApplicationBuilderABC):
self._startup: Optional[StartupABC] = None self._startup: Optional[StartupABC] = None
self._configuration = Configuration() self._configuration = Configuration()
self._runtime = ApplicationRuntime() self._environment = self._configuration.environment
self._services = ServiceProvider(self._configuration, self._runtime) self._services = ServiceCollection(self._configuration)
def use_startup(self, startup: Type[StartupABC]): def use_startup(self, startup: Type[StartupABC]):
""" """
@@ -28,7 +27,7 @@ class ApplicationBuilder(ApplicationBuilderABC):
:param startup: :param startup:
:return: :return:
""" """
self._startup = startup(self._configuration, self._runtime, self._services) self._startup = startup(self._configuration, self._services)
def build(self) -> ApplicationABC: def build(self) -> ApplicationABC:
""" """
@@ -39,4 +38,4 @@ class ApplicationBuilder(ApplicationBuilderABC):
self._startup.configure_configuration() self._startup.configure_configuration()
self._startup.configure_services() self._startup.configure_services()
return self._app(self._configuration, self._runtime, self._services) return self._app(self._configuration, self._services.build_service_provider())

View File

@@ -1,52 +0,0 @@
import pathlib
from datetime import datetime
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
class ApplicationRuntime(ApplicationRuntimeABC):
def __init__(self):
"""
Representation of the application runtime
"""
ApplicationRuntimeABC.__init__(self)
self._start_time: datetime = datetime.now()
self._end_time: datetime = datetime.now()
self._working_directory = pathlib.Path().absolute()
self._runtime_directory = pathlib.Path(__file__).parent.absolute()
@property
def start_time(self) -> datetime:
return self._start_time
@property
def end_time(self) -> datetime:
return self._end_time
@end_time.setter
def end_time(self, end_time: datetime):
self._end_time = end_time
@property
def date_time_now(self) -> datetime:
return datetime.now()
@property
def working_directory(self) -> str:
return self._working_directory
def set_working_directory(self, path: str = ''):
if path != '':
self._working_directory = path
return
self._working_directory = pathlib.Path().absolute()
@property
def runtime_directory(self) -> str:
return self._runtime_directory
def set_runtime_directory(self, file: str):
self._runtime_directory = pathlib.Path(file).parent.absolute()

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.configuration'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -27,4 +27,4 @@ from .configuration_variable_name_enum import ConfigurationVariableNameEnum
from .console_argument import ConsoleArgument from .console_argument import ConsoleArgument
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -11,7 +11,7 @@ from cpl.configuration.console_argument import ConsoleArgument
from cpl.console.console import Console from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.environment.application_environment import ApplicationEnvironment from cpl.environment.application_environment import ApplicationEnvironment
from cpl.environment.environment_abc import ApplicationEnvironmentABC from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl.environment.environment_name_enum import EnvironmentNameEnum from cpl.environment.environment_name_enum import EnvironmentNameEnum
@@ -85,7 +85,7 @@ class Configuration(ConfigurationABC):
Console.write_line(f'[{name}] {message}') Console.write_line(f'[{name}] {message}')
Console.set_foreground_color(ForegroundColorEnum.default) Console.set_foreground_color(ForegroundColorEnum.default)
def _set_variable(self, name: str, value: str): def _set_variable(self, name: str, value: any):
""" """
Sets variable to given value Sets variable to given value
:param name: :param name:
@@ -104,29 +104,6 @@ class Configuration(ConfigurationABC):
else: else:
self._config[name] = value self._config[name] = value
def _validate_argument_child(self, argument: str, argument_type: ConsoleArgument,
next_arguments: Optional[list[str]]) -> bool:
"""
Validates the child arguments of argument
:param argument:
:param argument_type:
:param next_arguments:
:return:
"""
if argument_type.console_arguments is not None and len(argument_type.console_arguments) > 0:
found = False
for child_argument_type in argument_type.console_arguments:
found = self._validate_argument_by_argument_type(argument, child_argument_type, next_arguments)
if found and child_argument_type.name not in self._additional_arguments:
self._additional_arguments.append(child_argument_type.name)
if not found:
raise Exception(f'Invalid argument: {argument}')
return found
return True
def _validate_argument_by_argument_type(self, argument: str, argument_type: ConsoleArgument, def _validate_argument_by_argument_type(self, argument: str, argument_type: ConsoleArgument,
next_arguments: list[str] = None) -> bool: next_arguments: list[str] = None) -> bool:
""" """
@@ -157,10 +134,16 @@ class Configuration(ConfigurationABC):
if argument_type.token != '' and argument.startswith(argument_type.token): if argument_type.token != '' and argument.startswith(argument_type.token):
# --new=value # --new=value
if len(argument.split(argument_type.token)[1].split(argument_type.value_token)) == 0:
raise Exception(f'Expected argument for command: {argument}')
argument_name = argument.split(argument_type.token)[1].split(argument_type.value_token)[0] argument_name = argument.split(argument_type.token)[1].split(argument_type.value_token)[0]
else: else:
# new=value # new=value
argument_name = argument.split(argument_type.token)[1] argument_name = argument.split(argument_type.value_token)[1]
if argument_name == '':
raise Exception(f'Expected argument for command: {argument_type.name}')
result = True result = True
@@ -191,7 +174,7 @@ class Configuration(ConfigurationABC):
# ?new value # ?new value
found = False found = False
for alias in argument_type.aliases: for alias in argument_type.aliases:
if alias in argument: if alias == argument or f' {alias} ' == argument:
found = True found = True
if argument_type.name not in argument and not found: if argument_type.name not in argument and not found:
@@ -199,12 +182,13 @@ class Configuration(ConfigurationABC):
if (next_arguments is None or len(next_arguments) == 0) and \ if (next_arguments is None or len(next_arguments) == 0) and \
argument_type.is_value_token_optional is not True: argument_type.is_value_token_optional is not True:
raise Exception(f'Invalid argument: {argument}') raise Exception(f'Expected argument for command: {argument_type.name}')
if (next_arguments is None or len(next_arguments) == 0) and argument_type.is_value_token_optional is True: if (next_arguments is None or len(next_arguments) == 0) and argument_type.is_value_token_optional is True:
value = '' value = ''
else: else:
value = next_arguments[0] value = next_arguments[0]
next_arguments.remove(value)
self._handled_args.append(value) self._handled_args.append(value)
if argument_type.token != '' and argument.startswith(argument_type.token): if argument_type.token != '' and argument.startswith(argument_type.token):
@@ -236,7 +220,23 @@ class Configuration(ConfigurationABC):
next_args = [] next_args = []
if len(next_arguments) > 1: if len(next_arguments) > 1:
next_args = next_arguments[1:] next_args = next_arguments[1:]
result = self._validate_argument_child(next_arguments[0], argument_type, next_args)
if argument_type.console_arguments is not None and len(argument_type.console_arguments) > 0:
found_child = False
for child_argument_type in argument_type.console_arguments:
found_child = self._validate_argument_by_argument_type(
next_arguments[0],
child_argument_type,
next_args
)
if found_child and child_argument_type.name not in self._additional_arguments:
self._additional_arguments.append(child_argument_type.name)
if found_child:
break
if not found_child:
result = self._validate_argument_by_argument_type(next_arguments[0], argument_type, next_args)
return result return result
@@ -277,6 +277,7 @@ class Configuration(ConfigurationABC):
if not found and error_message == '' and error is not False: if not found and error_message == '' and error is not False:
error_message = f'Invalid argument: {argument}' error_message = f'Invalid argument: {argument}'
if error_message != '':
if self._argument_error_function is not None: if self._argument_error_function is not None:
self._argument_error_function(error_message) self._argument_error_function(error_message)
else: else:
@@ -284,15 +285,25 @@ class Configuration(ConfigurationABC):
exit() exit()
def add_json_file(self, name: str, optional: bool = None, output: bool = True, path: str = None): add_args = []
path_root = self._application_environment.content_root_path for next_arg in next_arguments:
if path is not None: if next_arg not in self._handled_args and next_arg not in self._additional_arguments:
path_root = path add_args.append(next_arg)
if str(path_root).endswith('/') and not name.startswith('/'): self._set_variable(f'{argument}AdditionalArguments', add_args)
file_path = f'{path_root}{name}'
def add_json_file(self, name: str, optional: bool = None, output: bool = True, path: str = None):
if os.path.isabs(name):
file_path = name
else: else:
file_path = f'{path_root}/{name}' path_root = self._application_environment.working_directory
if path is not None:
path_root = path
if str(path_root).endswith('/') and not name.startswith('/'):
file_path = f'{path_root}{name}'
else:
file_path = f'{path_root}/{name}'
if not os.path.isfile(file_path): if not os.path.isfile(file_path):
if optional is not True: if optional is not True:
@@ -334,7 +345,7 @@ class Configuration(ConfigurationABC):
self._print_error(__name__, f'Cannot load config file: {file}! -> {e}') self._print_error(__name__, f'Cannot load config file: {file}! -> {e}')
return {} return {}
def add_configuration(self, key_type: type, value: ConfigurationModelABC): def add_configuration(self, key_type: Union[str, type], value: ConfigurationModelABC):
self._config[key_type] = value self._config[key_type] = value
def get_configuration(self, search_type: Union[str, Type[ConfigurationModelABC]]) -> Union[ def get_configuration(self, search_type: Union[str, Type[ConfigurationModelABC]]) -> Union[

View File

@@ -4,7 +4,7 @@ from typing import Type, Union, Optional
from cpl.configuration.console_argument import ConsoleArgument from cpl.configuration.console_argument import ConsoleArgument
from cpl.configuration.configuration_model_abc import ConfigurationModelABC from cpl.configuration.configuration_model_abc import ConfigurationModelABC
from cpl.environment.environment_abc import ApplicationEnvironmentABC from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
class ConfigurationABC(ABC): class ConfigurationABC(ABC):
@@ -72,7 +72,7 @@ class ConfigurationABC(ABC):
pass pass
@abstractmethod @abstractmethod
def add_configuration(self, key_type: type, value: object): def add_configuration(self, key_type: Union[str, type], value: object):
""" """
Add configuration object Add configuration object
:param key_type: :param key_type:
@@ -82,8 +82,7 @@ class ConfigurationABC(ABC):
pass pass
@abstractmethod @abstractmethod
def get_configuration(self, search_type: Union[str, Type[ConfigurationModelABC]]) -> Union[ def get_configuration(self, search_type: Union[str, Type[ConfigurationModelABC]]) -> Union[str, Callable[ConfigurationModelABC]]:
str, Callable[ConfigurationModelABC]]:
""" """
Returns value in configuration by given type Returns value in configuration by given type
:param search_type: :param search_type:

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.console'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -27,4 +27,4 @@ from .foreground_color_enum import ForegroundColorEnum
from .spinner_thread import SpinnerThread from .spinner_thread import SpinnerThread
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -446,6 +446,8 @@ class Console:
for call in cls._hold_back_calls: for call in cls._hold_back_calls:
call.function(*call.args) call.function(*call.args)
cls._hold_back_calls = []
time.sleep(0.1) time.sleep(0.1)
return return_value return return_value

View File

@@ -3,8 +3,8 @@
"Name": "sh_cpl", "Name": "sh_cpl",
"Version": { "Version": {
"Major": "2021", "Major": "2021",
"Minor": "04", "Minor": "4",
"Micro": "01-15" "Micro": "post1"
}, },
"Author": "Sven Heidemann", "Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de", "AuthorEmail": "sven.heidemann@sh-edraft.de",
@@ -22,8 +22,8 @@
"packaging==20.9", "packaging==20.9",
"pyfiglet==0.8.post1", "pyfiglet==0.8.post1",
"pynput==1.7.3", "pynput==1.7.3",
"SQLAlchemy==1.4.0", "SQLAlchemy==1.4.7",
"setuptools==54.1.2", "setuptools==56.0.0",
"tabulate==0.8.9", "tabulate==0.8.9",
"termcolor==1.1.0", "termcolor==1.1.0",
"watchdog==2.0.2", "watchdog==2.0.2",
@@ -34,10 +34,11 @@
"Classifiers": [] "Classifiers": []
}, },
"BuildSettings": { "BuildSettings": {
"SourcePath": "src", "ProjectType": "library",
"OutputPath": "dist", "SourcePath": "",
"Main": "cpl_cli.main", "OutputPath": "../../dist",
"EntryPoint": "cpl", "Main": "",
"EntryPoint": "",
"IncludePackageData": true, "IncludePackageData": true,
"Included": [ "Included": [
"*/templates" "*/templates"
@@ -47,10 +48,7 @@
"*/logs", "*/logs",
"*/tests" "*/tests"
], ],
"PackageData": { "PackageData": {},
"cpl_cli": [ "ProjectReferences": []
"*.json"
]
}
} }
} }

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.database'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -25,4 +25,4 @@ from .database_settings import DatabaseSettings
from .database_settings_name_enum import DatabaseSettingsNameEnum from .database_settings_name_enum import DatabaseSettingsNameEnum
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.database.connection'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -24,4 +24,4 @@ from .database_connection import DatabaseConnection
from .database_connection_abc import DatabaseConnectionABC from .database_connection_abc import DatabaseConnectionABC
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.database.context'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -24,4 +24,4 @@ from .database_context import DatabaseContext
from .database_context_abc import DatabaseContextABC from .database_context_abc import DatabaseContextABC
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -30,6 +30,9 @@ class DatabaseContext(DatabaseContextABC):
self._db.connect(connection_string) self._db.connect(connection_string)
self._create_tables() self._create_tables()
def save_changes(self):
self._db.session.commit()
def _create_tables(self): def _create_tables(self):
try: try:
for subclass in DatabaseModel.__subclasses__(): for subclass in DatabaseModel.__subclasses__():

View File

@@ -6,6 +6,10 @@ from sqlalchemy.orm import Session
class DatabaseContextABC(ABC): class DatabaseContextABC(ABC):
@abstractmethod
def __init__(self, *args):
pass
@property @property
@abstractmethod @abstractmethod
def engine(self) -> engine: pass def engine(self) -> engine: pass
@@ -23,6 +27,12 @@ class DatabaseContextABC(ABC):
""" """
pass pass
def save_changes(self):
"""
Saves changes of the database
"""
pass
@abstractmethod @abstractmethod
def _create_tables(self): def _create_tables(self):
""" """

View File

@@ -15,14 +15,17 @@ __title__ = 'cpl.dependency_injection'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
from .service_abc import ServiceABC from .service_collection import ServiceCollection
from .service_collection_abc import ServiceCollectionABC
from .service_descriptor import ServiceDescriptor
from .service_lifetime_enum import ServiceLifetimeEnum
from .service_provider import ServiceProvider from .service_provider import ServiceProvider
from .service_provider_abc import ServiceProviderABC from .service_provider_abc import ServiceProviderABC
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -1,11 +0,0 @@
from abc import ABC, abstractmethod
class ServiceABC(ABC):
@abstractmethod
def __init__(self):
"""
ABC to represent a service
"""
pass

View File

@@ -0,0 +1,70 @@
from typing import Union, Type, Callable, Optional
from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.database.database_settings import DatabaseSettings
from cpl.database.context.database_context_abc import DatabaseContextABC
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
from cpl.dependency_injection.service_collection_abc import ServiceCollectionABC
from cpl.dependency_injection.service_descriptor import ServiceDescriptor
from cpl.dependency_injection.service_lifetime_enum import ServiceLifetimeEnum
from cpl.dependency_injection.service_provider import ServiceProvider
from cpl.logging.logger_service import Logger
from cpl.logging.logger_abc import LoggerABC
from cpl.utils.credential_manager import CredentialManager
class ServiceCollection(ServiceCollectionABC):
def __init__(self, config: ConfigurationABC):
ServiceCollectionABC.__init__(self)
self._configuration: ConfigurationABC = config
self._database_context: Optional[DatabaseContextABC] = None
self._service_descriptors: list[ServiceDescriptor] = []
def _add_descriptor(self, service: Union[type, object], lifetime: ServiceLifetimeEnum):
found = False
for descriptor in self._service_descriptors:
if isinstance(service, descriptor.service_type):
found = True
if found:
service_type = service
if not isinstance(service, type):
service_type = type(service)
raise Exception(f'Service of type {service_type} already exists')
self._service_descriptors.append(ServiceDescriptor(service, lifetime))
def add_db_context(self, db_context_type: Type[DatabaseContextABC], db_settings: DatabaseSettings):
self._database_context = db_context_type(db_settings)
self._database_context.connect(CredentialManager.build_string(db_settings.connection_string, db_settings.credentials))
def add_logging(self):
self.add_singleton(LoggerABC, Logger)
def add_singleton(self, service_type: Union[type, object], service: Union[type, object] = None):
impl = None
if service is not None:
if isinstance(service, type):
impl = self.build_service_provider().build_service(service)
self._add_descriptor(impl, ServiceLifetimeEnum.singleton)
else:
if isinstance(service_type, type):
impl = self.build_service_provider().build_service(service_type)
self._add_descriptor(impl, ServiceLifetimeEnum.singleton)
def add_scoped(self, service_type: Type, service: Callable = None):
raise Exception('Not implemented')
def add_transient(self, service_type: Union[type], service: Union[type] = None):
if service is not None:
self._add_descriptor(service, ServiceLifetimeEnum.transient)
else:
self._add_descriptor(service_type, ServiceLifetimeEnum.transient)
def build_service_provider(self) -> ServiceProviderABC:
return ServiceProvider(self._service_descriptors, self._configuration, self._database_context)

View File

@@ -0,0 +1,71 @@
from abc import abstractmethod, ABC
from collections import Callable
from typing import Type
from cpl.database.database_settings import DatabaseSettings
from cpl.database.context.database_context_abc import DatabaseContextABC
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
class ServiceCollectionABC(ABC):
@abstractmethod
def __init__(self):
"""
ABC for service providing
"""
pass
@abstractmethod
def add_db_context(self, db_context: Type[DatabaseContextABC], db_settings: DatabaseSettings):
"""
Adds database context
:param db_context:
:param db_settings:
:return:
"""
pass
@abstractmethod
def add_logging(self):
"""
Adds the CPL internal logger
"""
pass
@abstractmethod
def add_transient(self, service_type: Type, service: Callable = None):
"""
Adds a service with transient lifetime
:param service_type:
:param service:
:return:
"""
pass
@abstractmethod
def add_scoped(self, service_type: Type, service: Callable = None):
"""
Adds a service with scoped lifetime
:param service_type:
:param service:
:return:
"""
pass
@abstractmethod
def add_singleton(self, service_type: Type, service: Callable = None):
"""
Adds a service with singleton lifetime
:param service_type:
:param service:
:return:
"""
pass
@abstractmethod
def build_service_provider(self) -> ServiceProviderABC:
"""
Creates instance of the service provider
"""
pass

View File

@@ -0,0 +1,33 @@
from typing import Union, Optional
from cpl.dependency_injection.service_lifetime_enum import ServiceLifetimeEnum
class ServiceDescriptor:
def __init__(self, implementation: Union[type, Optional[object]], lifetime: ServiceLifetimeEnum):
self._service_type = implementation
self._implementation = implementation
self._lifetime = lifetime
if not isinstance(implementation, type):
self._service_type = type(implementation)
else:
self._implementation = None
@property
def service_type(self) -> type:
return self._service_type
@property
def implementation(self) -> Union[type, Optional[object]]:
return self._implementation
@implementation.setter
def implementation(self, implementation: Union[type, Optional[object]]):
self._implementation = implementation
@property
def lifetime(self) -> ServiceLifetimeEnum:
return self._lifetime

View File

@@ -0,0 +1,8 @@
from enum import Enum
class ServiceLifetimeEnum(Enum):
singleton = 0
scoped = 1 # not supported yet
transient = 2

View File

@@ -1,45 +1,61 @@
from collections import Callable from collections import Callable
from inspect import signature, Parameter from inspect import signature, Parameter
from typing import Type, Optional, Union from typing import Optional
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
from cpl.configuration.configuration_abc import ConfigurationABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.configuration.configuration_model_abc import ConfigurationModelABC from cpl.configuration.configuration_model_abc import ConfigurationModelABC
from cpl.database.context.database_context_abc import DatabaseContextABC from cpl.database.context.database_context_abc import DatabaseContextABC
from cpl.dependency_injection.service_abc import ServiceABC
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
from cpl.environment.environment_abc import ApplicationEnvironmentABC from cpl.dependency_injection.service_descriptor import ServiceDescriptor
from cpl.dependency_injection.service_lifetime_enum import ServiceLifetimeEnum
from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
class ServiceProvider(ServiceProviderABC): class ServiceProvider(ServiceProviderABC):
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC): def __init__(self, service_descriptors: list[ServiceDescriptor], config: ConfigurationABC, db_context: Optional[DatabaseContextABC]):
"""
Service for service providing
:param runtime:
"""
ServiceProviderABC.__init__(self) ServiceProviderABC.__init__(self)
self._service_descriptors: list[ServiceDescriptor] = service_descriptors
self._configuration: ConfigurationABC = config self._configuration: ConfigurationABC = config
self._runtime: ApplicationRuntimeABC = runtime self._database_context = db_context
self._database_context: Optional[DatabaseContextABC] = None
self._transient_services: dict[Type[ServiceABC], Callable[ServiceABC]] = {} def _find_service(self, service_type: type) -> [ServiceDescriptor]:
self._scoped_services: dict[Type[ServiceABC], Callable[ServiceABC]] = {} for descriptor in self._service_descriptors:
self._singleton_services: dict[Type[ServiceABC], Callable[ServiceABC], ServiceABC] = {} if descriptor.service_type == service_type or issubclass(descriptor.service_type, service_type):
return descriptor
def _create_instance(self, service: Union[Callable[ServiceABC], ServiceABC]) -> Callable[ServiceABC]: return None
"""
Creates an instance of given type def _get_service(self, parameter: Parameter) -> object:
:param service: for descriptor in self._service_descriptors:
:return: if descriptor.service_type == parameter.annotation or issubclass(descriptor.service_type, parameter.annotation):
""" if descriptor.implementation is not None:
sig = signature(service.__init__) return descriptor.implementation
implementation = self.build_service(descriptor.service_type)
if descriptor.lifetime == ServiceLifetimeEnum.singleton:
descriptor.implementation = implementation
return implementation
def build_service(self, service_type: type) -> object:
for descriptor in self._service_descriptors:
if descriptor.service_type == service_type or issubclass(descriptor.service_type, service_type):
if descriptor.implementation is not None:
service_type = type(descriptor.implementation)
else:
service_type = descriptor.service_type
break
sig = signature(service_type.__init__)
params = [] params = []
for param in sig.parameters.items(): for param in sig.parameters.items():
parameter = param[1] parameter = param[1]
if parameter.name != 'self' and parameter.annotation != Parameter.empty: if parameter.name != 'self' and parameter.annotation != Parameter.empty:
if issubclass(parameter.annotation, ApplicationRuntimeABC): if issubclass(parameter.annotation, ServiceProviderABC):
params.append(self._runtime) params.append(self)
elif issubclass(parameter.annotation, ApplicationEnvironmentABC): elif issubclass(parameter.annotation, ApplicationEnvironmentABC):
params.append(self._configuration.environment) params.append(self._configuration.environment)
@@ -53,70 +69,22 @@ class ServiceProvider(ServiceProviderABC):
elif issubclass(parameter.annotation, ConfigurationABC): elif issubclass(parameter.annotation, ConfigurationABC):
params.append(self._configuration) params.append(self._configuration)
elif issubclass(parameter.annotation, ServiceProviderABC):
params.append(self)
else: else:
params.append(self.get_service(parameter.annotation)) params.append(self._get_service(parameter))
return service(*params) return service_type(*params)
def add_db_context(self, db_context: Type[DatabaseContextABC]): def get_service(self, service_type: type) -> Optional[Callable[object]]:
self._database_context = self._create_instance(db_context) result = self._find_service(service_type)
def get_db_context(self) -> Callable[DatabaseContextABC]: if result is None:
return self._database_context return None
def add_transient(self, service_type: Type[ServiceABC], service: Callable[ServiceABC] = None): if result.implementation is not None:
if service is None: return result.implementation
self._transient_services[service_type] = service_type
else:
self._transient_services[service_type] = service
def add_scoped(self, service_type: Type[ServiceABC], service: Callable[ServiceABC] = None): implementation = self.build_service(service_type)
if service is None: if result.lifetime == ServiceLifetimeEnum.singleton:
self._scoped_services[service_type] = service_type result.implementation = implementation
else:
self._scoped_services[service_type] = service
def add_singleton(self, service_type: Type[ServiceABC], service: Callable[ServiceABC] = None): return implementation
for known_service in self._singleton_services:
if type(known_service) == service_type:
raise Exception(f'Service with type {service_type} already exists')
if service is None:
self._singleton_services[service_type] = self._create_instance(service_type)
else:
self._singleton_services[service_type] = self._create_instance(service)
def get_service(self, instance_type: Type) -> Callable[ServiceABC]:
if issubclass(instance_type, ServiceProviderABC):
return self
for service in self._transient_services:
if service == instance_type and isinstance(self._transient_services[service], type(instance_type)):
return self._create_instance(self._transient_services[service])
for service in self._scoped_services:
if service == instance_type and isinstance(self._scoped_services[service], type(instance_type)):
return self._create_instance(self._scoped_services[service])
for service in self._singleton_services:
if service == instance_type and isinstance(self._singleton_services[service], instance_type):
return self._singleton_services[service]
def remove_service(self, instance_type: Type[ServiceABC]):
for service in self._transient_services:
if service == instance_type and isinstance(self._transient_services[service], type(instance_type)):
del self._transient_services[service]
return
for service in self._scoped_services:
if service == instance_type and isinstance(self._scoped_services[service], type(instance_type)):
del self._scoped_services[service]
return
for service in self._singleton_services:
if service == instance_type and isinstance(self._singleton_services[service], instance_type):
del self._singleton_services[service]
return

View File

@@ -1,9 +1,6 @@
from abc import abstractmethod, ABC from abc import abstractmethod, ABC
from collections import Callable from collections import Callable
from typing import Type from typing import Type, Optional
from cpl.database.context.database_context_abc import DatabaseContextABC
from cpl.dependency_injection.service_abc import ServiceABC
class ServiceProviderABC(ABC): class ServiceProviderABC(ABC):
@@ -16,66 +13,19 @@ class ServiceProviderABC(ABC):
pass pass
@abstractmethod @abstractmethod
def add_db_context(self, db_context: Type[DatabaseContextABC]): def build_service(self, service_type: type) -> object:
""" """
Adds database context Creates instance of given type
:param db_context:
:return:
"""
pass
@abstractmethod
def get_db_context(self) -> Callable[DatabaseContextABC]:
""""
Returns database context
:return Callable[DatabaseContextABC]:
"""
pass
@abstractmethod
def add_transient(self, service_type: Type, service: Callable = None):
"""
Adds a service with transient lifetime
:param service_type: :param service_type:
:param service:
:return: :return:
""" """
pass pass
@abstractmethod @abstractmethod
def add_scoped(self, service_type: Type, service: Callable = None): def get_service(self, instance_type: Type) -> Optional[Callable[object]]:
"""
Adds a service with scoped lifetime
:param service_type:
:param service:
:return:
"""
pass
@abstractmethod
def add_singleton(self, service_type: Type, service: Callable = None):
"""
Adds a service with singleton lifetime
:param service_type:
:param service:
:return:
"""
pass
@abstractmethod
def get_service(self, instance_type: Type) -> Callable[ServiceABC]:
""" """
Returns instance of given type Returns instance of given type
:param instance_type: :param instance_type:
:return: :return:
""" """
pass pass
@abstractmethod
def remove_service(self, instance_type: type):
"""
Removes service
:param instance_type:
:return:
"""
pass

View File

@@ -15,14 +15,14 @@ __title__ = 'cpl.environment'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
from .environment_abc import ApplicationEnvironmentABC from .application_environment_abc import ApplicationEnvironmentABC
from .environment_name_enum import EnvironmentNameEnum from .environment_name_enum import EnvironmentNameEnum
from .application_environment import ApplicationEnvironment from .application_environment import ApplicationEnvironment
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -1,24 +1,29 @@
import os
from datetime import datetime
from socket import gethostname from socket import gethostname
from typing import Optional from typing import Optional
from cpl.environment.environment_abc import ApplicationEnvironmentABC from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl.environment.environment_name_enum import EnvironmentNameEnum from cpl.environment.environment_name_enum import EnvironmentNameEnum
class ApplicationEnvironment(ApplicationEnvironmentABC): class ApplicationEnvironment(ApplicationEnvironmentABC):
def __init__(self, name: EnvironmentNameEnum = EnvironmentNameEnum.production, crp: str = './'): def __init__(self, name: EnvironmentNameEnum = EnvironmentNameEnum.production):
""" """
Represents environment of the application Represents environment of the application
:param name: :param name:
:param crp:
""" """
ApplicationEnvironmentABC.__init__(self) ApplicationEnvironmentABC.__init__(self)
self._environment_name: Optional[EnvironmentNameEnum] = name self._environment_name: Optional[EnvironmentNameEnum] = name
self._app_name: Optional[str] = None self._app_name: Optional[str] = None
self._customer: Optional[str] = None self._customer: Optional[str] = None
self._content_root_path: Optional[str] = crp
self._start_time: datetime = datetime.now()
self._end_time: datetime = datetime.now()
self._runtime_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
self._working_directory = os.getcwd()
@property @property
def environment_name(self) -> str: def environment_name(self) -> str:
@@ -44,14 +49,46 @@ class ApplicationEnvironment(ApplicationEnvironmentABC):
def customer(self, customer: str): def customer(self, customer: str):
self._customer = customer self._customer = customer
@property
def content_root_path(self) -> str:
return self._content_root_path
@content_root_path.setter
def content_root_path(self, content_root_path: str):
self._content_root_path = content_root_path
@property @property
def host_name(self): def host_name(self):
return gethostname() return gethostname()
@property
def start_time(self) -> datetime:
return self._start_time
@property
def end_time(self) -> datetime:
return self._end_time
@end_time.setter
def end_time(self, end_time: datetime):
self._end_time = end_time
@property
def date_time_now(self) -> datetime:
return datetime.now()
@property
def working_directory(self) -> str:
return str(self._working_directory)
@property
def runtime_directory(self) -> str:
return str(self._runtime_directory)
def set_runtime_directory(self, runtime_directory: str):
if runtime_directory != '':
self._runtime_directory = runtime_directory
return
self._runtime_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def set_working_directory(self, working_directory: str):
if working_directory != '':
self._working_directory = working_directory
os.chdir(self._working_directory)
return
self._working_directory = os.path.abspath('./')
os.chdir(self._working_directory)

View File

@@ -2,15 +2,43 @@ from abc import ABC, abstractmethod
from datetime import datetime from datetime import datetime
class ApplicationRuntimeABC(ABC): class ApplicationEnvironmentABC(ABC):
@abstractmethod @abstractmethod
def __init__(self): def __init__(self):
""" """
ABC for application runtime ABC of application environment
""" """
pass pass
@property
@abstractmethod
def environment_name(self) -> str: pass
@environment_name.setter
@abstractmethod
def environment_name(self, environment_name: str): pass
@property
@abstractmethod
def application_name(self) -> str: pass
@application_name.setter
@abstractmethod
def application_name(self, application_name: str): pass
@property
@abstractmethod
def customer(self) -> str: pass
@customer.setter
@abstractmethod
def customer(self, customer: str): pass
@property
@abstractmethod
def host_name(self) -> str: pass
@property @property
@abstractmethod @abstractmethod
def start_time(self) -> datetime: pass def start_time(self) -> datetime: pass
@@ -47,3 +75,12 @@ class ApplicationRuntimeABC(ABC):
:return: :return:
""" """
pass pass
@abstractmethod
def set_working_directory(self, working_directory: str):
"""
Sets the current working directory
:param working_directory:
:return:
"""
pass

View File

@@ -1,47 +0,0 @@
from abc import ABC, abstractmethod
class ApplicationEnvironmentABC(ABC):
@abstractmethod
def __init__(self):
"""
ABC of application environment
"""
pass
@property
@abstractmethod
def environment_name(self) -> str: pass
@environment_name.setter
@abstractmethod
def environment_name(self, environment_name: str): pass
@property
@abstractmethod
def application_name(self) -> str: pass
@application_name.setter
@abstractmethod
def application_name(self, application_name: str): pass
@property
@abstractmethod
def customer(self) -> str: pass
@customer.setter
@abstractmethod
def customer(self, customer: str): pass
@property
@abstractmethod
def content_root_path(self) -> str: pass
@content_root_path.setter
@abstractmethod
def content_root_path(self, content_root_path: str): pass
@property
@abstractmethod
def host_name(self) -> str: pass

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.logging'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -27,4 +27,4 @@ from .logging_settings import LoggingSettings
from .logging_settings_name_enum import LoggingSettingsNameEnum from .logging_settings_name_enum import LoggingSettingsNameEnum
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -1,16 +1,14 @@
from abc import abstractmethod from abc import abstractmethod, ABC
from cpl.dependency_injection.service_abc import ServiceABC
class LoggerABC(ServiceABC): class LoggerABC(ABC):
@abstractmethod @abstractmethod
def __init__(self): def __init__(self):
""" """
ABC for logging ABC for logging
""" """
ServiceABC.__init__(self) ABC.__init__(self)
@abstractmethod @abstractmethod
def header(self, string: str): def header(self, string: str):

View File

@@ -3,9 +3,9 @@ import os
import traceback import traceback
from string import Template from string import Template
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
from cpl.console.console import Console from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl.logging.logger_abc import LoggerABC from cpl.logging.logger_abc import LoggerABC
from cpl.logging.logging_level_enum import LoggingLevelEnum from cpl.logging.logging_level_enum import LoggingLevelEnum
from cpl.logging.logging_settings import LoggingSettings from cpl.logging.logging_settings import LoggingSettings
@@ -14,7 +14,7 @@ from cpl.time.time_format_settings import TimeFormatSettings
class Logger(LoggerABC): class Logger(LoggerABC):
def __init__(self, logging_settings: LoggingSettings, time_format: TimeFormatSettings, app_runtime: ApplicationRuntimeABC): def __init__(self, logging_settings: LoggingSettings, time_format: TimeFormatSettings, env: ApplicationEnvironmentABC):
""" """
Service for logging Service for logging
:param logging_settings: :param logging_settings:
@@ -23,13 +23,13 @@ class Logger(LoggerABC):
""" """
LoggerABC.__init__(self) LoggerABC.__init__(self)
self._app_runtime = app_runtime self._env = env
self._log_settings: LoggingSettings = logging_settings self._log_settings: LoggingSettings = logging_settings
self._time_format_settings: TimeFormatSettings = time_format self._time_format_settings: TimeFormatSettings = time_format
self._log = Template(self._log_settings.filename).substitute( self._log = Template(self._log_settings.filename).substitute(
date_time_now=self._app_runtime.date_time_now.strftime(self._time_format_settings.date_time_format), date_time_now=self._env.date_time_now.strftime(self._time_format_settings.date_time_format),
start_time=self._app_runtime.start_time.strftime(self._time_format_settings.date_time_log_format) start_time=self._env.start_time.strftime(self._time_format_settings.date_time_log_format)
) )
self._path = self._log_settings.path self._path = self._log_settings.path
self._level = self._log_settings.level self._level = self._log_settings.level

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.mailing'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -27,4 +27,4 @@ from .email_client_settings import EMailClientSettings
from .email_client_settings_name_enum import EMailClientSettingsNameEnum from .email_client_settings_name_enum import EMailClientSettingsNameEnum
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -1,17 +1,16 @@
from abc import abstractmethod from abc import abstractmethod, ABC
from cpl.dependency_injection.service_abc import ServiceABC
from cpl.mailing.email import EMail from cpl.mailing.email import EMail
class EMailClientABC(ServiceABC): class EMailClientABC(ABC):
@abstractmethod @abstractmethod
def __init__(self): def __init__(self):
""" """
ABC to send emails ABC to send emails
""" """
ServiceABC.__init__(self) ABC.__init__(self)
@abstractmethod @abstractmethod
def connect(self): def connect(self):

View File

@@ -2,7 +2,7 @@ import ssl
from smtplib import SMTP from smtplib import SMTP
from typing import Optional from typing import Optional
from cpl.environment.environment_abc import ApplicationEnvironmentABC from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl.logging.logger_abc import LoggerABC from cpl.logging.logger_abc import LoggerABC
from cpl.mailing.email import EMail from cpl.mailing.email import EMail
from cpl.mailing.email_client_abc import EMailClientABC from cpl.mailing.email_client_abc import EMailClientABC

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.time'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -24,4 +24,4 @@ from .time_format_settings import TimeFormatSettings
from .time_format_settings_names_enum import TimeFormatSettingsNamesEnum from .time_format_settings_names_enum import TimeFormatSettingsNamesEnum
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -15,7 +15,7 @@ __title__ = 'cpl.utils'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4.post1'
from collections import namedtuple from collections import namedtuple
@@ -25,4 +25,4 @@ from .string import String
from .pip import Pip from .pip import Pip
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='post1')

View File

@@ -1,6 +1,9 @@
import os
import subprocess import subprocess
import sys import sys
import shlex
from contextlib import suppress from contextlib import suppress
from textwrap import dedent
from typing import Optional from typing import Optional
@@ -9,6 +12,8 @@ class Pip:
Executes pip commands Executes pip commands
""" """
_executable = sys.executable _executable = sys.executable
_env = os.environ
_is_venv = False
""" """
Getter Getter
@@ -29,8 +34,17 @@ class Pip:
:param executable: :param executable:
:return: :return:
""" """
if executable is not None: if executable is not None and executable != sys.executable:
cls._executable = executable cls._executable = executable
if os.path.islink(cls._executable):
cls._is_venv = True
path = os.path.dirname(os.path.dirname(cls._executable))
cls._env = os.environ
if sys.platform == 'win32':
cls._env['PATH'] = f'{path}\\bin' + os.pathsep + os.environ.get('PATH', '')
else:
cls._env['PATH'] = f'{path}/bin' + os.pathsep + os.environ.get('PATH', '')
cls._env['VIRTUAL_ENV'] = path
@classmethod @classmethod
def reset_executable(cls): def reset_executable(cls):
@@ -39,6 +53,7 @@ class Pip:
:return: :return:
""" """
cls._executable = sys.executable cls._executable = sys.executable
cls._is_venv = False
""" """
Public utils functions Public utils functions
@@ -53,7 +68,14 @@ class Pip:
""" """
result = None result = None
with suppress(Exception): with suppress(Exception):
result = subprocess.check_output([cls._executable, "-m", "pip", "show", package], stderr=subprocess.DEVNULL) args = [cls._executable, "-m", "pip", "show", package]
if cls._is_venv:
args = ["pip", "show", package]
result = subprocess.check_output(
args,
stderr=subprocess.DEVNULL, env=cls._env
)
if result is None: if result is None:
return None return None
@@ -76,7 +98,11 @@ class Pip:
Gets table of outdated packages Gets table of outdated packages
:return: :return:
""" """
return subprocess.check_output([cls._executable, "-m", "pip", "list", "--outdated"]) args = [cls._executable, "-m", "pip", "list", "--outdated"]
if cls._is_venv:
args = ["pip", "list", "--outdated"]
return subprocess.check_output(args, env=cls._env)
@classmethod @classmethod
def install(cls, package: str, *args, source: str = None, stdout=None, stderr=None): def install(cls, package: str, *args, source: str = None, stdout=None, stderr=None):
@@ -90,6 +116,8 @@ class Pip:
:return: :return:
""" """
pip_args = [cls._executable, "-m", "pip", "install"] pip_args = [cls._executable, "-m", "pip", "install"]
if cls._is_venv:
pip_args = ["pip", "install"]
for arg in args: for arg in args:
pip_args.append(arg) pip_args.append(arg)
@@ -99,7 +127,7 @@ class Pip:
pip_args.append(source) pip_args.append(source)
pip_args.append(package) pip_args.append(package)
subprocess.run(pip_args, stdout=stdout, stderr=stderr) subprocess.run(pip_args, stdout=stdout, stderr=stderr, env=cls._env)
@classmethod @classmethod
def uninstall(cls, package: str, stdout=None, stderr=None): def uninstall(cls, package: str, stdout=None, stderr=None):
@@ -110,4 +138,11 @@ class Pip:
:param stderr: :param stderr:
:return: :return:
""" """
subprocess.run([cls._executable, "-m", "pip", "uninstall", "--yes", package], stdout=stdout, stderr=stderr) args = [cls._executable, "-m", "pip", "uninstall", "--yes", package]
if cls._is_venv:
args = ["pip", "uninstall", "--yes", package]
subprocess.run(
args,
stdout=stdout, stderr=stderr, env=cls._env
)

View File

@@ -1,4 +1,6 @@
import re import re
import string
import random
class String: class String:
@@ -7,31 +9,58 @@ class String:
""" """
@staticmethod @staticmethod
def convert_to_snake_case(name: str) -> str: def convert_to_camel_case(chars: str) -> str:
""" """
Converts string to snake case Converts string to camel case
:param name: :param chars:
:return: :return:
""" """
converted_name = chars
char_set = string.punctuation + ' '
for char in char_set:
if char in converted_name:
converted_name = ''.join(word.title() for word in converted_name.split(char))
return converted_name
@staticmethod
def convert_to_snake_case(chars: str) -> str:
"""
Converts string to snake case
:param chars:
:return:
"""
# convert to train-case to CamelCase
if '-' in chars:
chars = ''.join(word.title() for word in chars.split('-'))
pattern1 = re.compile(r'(.)([A-Z][a-z]+)') pattern1 = re.compile(r'(.)([A-Z][a-z]+)')
pattern2 = re.compile(r'([a-z0-9])([A-Z])') pattern2 = re.compile(r'([a-z0-9])([A-Z])')
file_name = re.sub(pattern1, r'\1_\2', name) file_name = re.sub(pattern1, r'\1_\2', chars)
return re.sub(pattern2, r'\1_\2', file_name).lower() return re.sub(pattern2, r'\1_\2', file_name).lower()
@staticmethod @staticmethod
def first_to_upper(string: str) -> str: def first_to_upper(chars: str) -> str:
""" """
Converts first char to upper Converts first char to upper
:param string: :param chars:
:return: :return:
""" """
return f'{string[0].upper()}{string[1:]}' return f'{chars[0].upper()}{chars[1:]}'
@staticmethod @staticmethod
def first_to_lower(string: str) -> str: def first_to_lower(chars: str) -> str:
""" """
Converts first char to lower Converts first char to lower
:param string: :param chars:
:return: :return:
""" """
return f'{string[0].lower()}{string[1:]}' return f'{chars[0].lower()}{chars[1:]}'
@staticmethod
def random_string(chars: str, length: int) -> str:
"""
Creates random string by given chars and length
"""
return ''.join(random.choice(chars) for _ in range(length))

View File

@@ -1,21 +1,21 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
""" """
__title__ = 'sh_cpl.cpl_cli' __title__ = 'cpl_cli'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
@@ -29,4 +29,4 @@ from .main import main
from .startup import Startup from .startup import Startup
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

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

View File

@@ -1,15 +1,16 @@
from typing import Optional from typing import Optional
from cpl.application.application_abc import ApplicationABC from cpl.application.application_abc import ApplicationABC
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
from cpl.configuration.configuration_abc import ConfigurationABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.console import Console from cpl.console.console import Console
from cpl.dependency_injection import ServiceProviderABC from cpl.dependency_injection import ServiceProviderABC
from cpl_cli.command.add_service import AddService
from cpl_cli.command.build_service import BuildService from cpl_cli.command.build_service import BuildService
from cpl_cli.command.generate_service import GenerateService from cpl_cli.command.generate_service import GenerateService
from cpl_cli.command.install_service import InstallService from cpl_cli.command.install_service import InstallService
from cpl_cli.command.new_service import NewService from cpl_cli.command.new_service import NewService
from cpl_cli.command.publish_service import PublishService from cpl_cli.command.publish_service import PublishService
from cpl_cli.command.remove_service import RemoveService
from cpl_cli.command.start_service import StartService from cpl_cli.command.start_service import StartService
from cpl_cli.command.uninstall_service import UninstallService from cpl_cli.command.uninstall_service import UninstallService
from cpl_cli.command.update_service import UpdateService from cpl_cli.command.update_service import UpdateService
@@ -22,27 +23,33 @@ from cpl_cli.command.version_service import VersionService
class CLI(ApplicationABC): class CLI(ApplicationABC):
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC, services: ServiceProviderABC): def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
""" """
CPL CLI CPL CLI
""" """
ApplicationABC.__init__(self, config, runtime, services) ApplicationABC.__init__(self, config, services)
self._command_handler: Optional[CommandHandler] = None self._command_handler: Optional[CommandHandler] = None
self._options: list[str] = []
def configure(self): def configure(self):
self._command_handler: CommandHandler = self._services.get_service(CommandHandler) self._command_handler: CommandHandler = self._services.get_service(CommandHandler)
self._command_handler.add_command(CommandModel('build', ['h', 'B'], BuildService, True)) self._command_handler.add_command(CommandModel('add', ['a', 'a'], AddService, False, False, False))
self._command_handler.add_command(CommandModel('generate', ['g', 'G'], GenerateService, True)) self._command_handler.add_command(CommandModel('build', ['b', 'B'], BuildService, False, True, True))
self._command_handler.add_command(CommandModel('help', ['h', 'H'], HelpService, False)) self._command_handler.add_command(CommandModel('generate', ['g', 'G'], GenerateService, False, True, False))
self._command_handler.add_command(CommandModel('install', ['i', 'I'], InstallService, True)) self._command_handler.add_command(CommandModel('help', ['h', 'H'], HelpService, False, False, False))
self._command_handler.add_command(CommandModel('new', ['n', 'N'], NewService, False)) self._command_handler.add_command(CommandModel('install', ['i', 'I'], InstallService, False, True, True))
self._command_handler.add_command(CommandModel('publish', ['p', 'P'], PublishService, True)) self._command_handler.add_command(CommandModel('new', ['n', 'N'], NewService, False, False, True))
self._command_handler.add_command(CommandModel('start', ['s', 'S'], StartService, True)) self._command_handler.add_command(CommandModel('publish', ['p', 'P'], PublishService, False, True, True))
self._command_handler.add_command(CommandModel('uninstall', ['ui', 'UI'], UninstallService, True)) self._command_handler.add_command(CommandModel('remove', ['r', 'R'], RemoveService, True, True, False))
self._command_handler.add_command(CommandModel('update', ['u', 'U'], UpdateService, True)) self._command_handler.add_command(CommandModel('start', ['s', 'S'], StartService, False, True, True))
self._command_handler.add_command(CommandModel('version', ['v', 'V'], VersionService, False)) self._command_handler.add_command(CommandModel('uninstall', ['ui', 'UI'], UninstallService, False, True, True))
self._command_handler.add_command(CommandModel('update', ['u', 'U'], UpdateService, False, True, True))
self._command_handler.add_command(CommandModel('version', ['v', 'V'], VersionService, False, False, False))
self._command_handler.add_command(CommandModel('--help', ['-h', '-H'], HelpService, False, False, False))
self._options.append('--help')
def main(self): def main(self):
""" """
@@ -53,16 +60,46 @@ class CLI(ApplicationABC):
command = None command = None
args = [] args = []
if len(self._configuration.additional_arguments) > 0: if len(self._configuration.additional_arguments) > 0:
command = self._configuration.additional_arguments[0] is_option = False
if len(self._configuration.additional_arguments) > 1: for opt in self._options:
args = self._configuration.additional_arguments[1:] if opt in self._configuration.additional_arguments:
is_option = True
command = opt
args = self._configuration.additional_arguments
args.remove(opt)
if not is_option:
command = self._configuration.additional_arguments[0]
if len(self._configuration.additional_arguments) > 1:
args = self._configuration.additional_arguments[1:]
else: else:
for cmd in self._command_handler.commands: for cmd in self._command_handler.commands:
result = self._configuration.get_configuration(cmd.name) result = self._configuration.get_configuration(cmd.name)
if result is not None: result_args: list[str] = self._configuration.get_configuration(f'{cmd.name}AdditionalArguments')
is_option = False
for opt in self._options:
if opt == result:
is_option = True
command = opt
elif result_args is not None and opt in result_args:
is_option = True
command = opt
result_args.remove(opt)
if is_option:
args.append(cmd.name)
if result_args is not None:
for arg in result_args:
args.append(arg)
elif result is not None:
command = cmd.name command = cmd.name
args.append(result) args.append(result)
for arg in result_args:
args.append(arg)
if command is None: if command is None:
Error.error(f'Expected command') Error.error(f'Expected command')
return return

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
@@ -15,7 +15,7 @@ __title__ = 'cpl_cli.command'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
@@ -28,4 +28,4 @@ from .publish_service import PublishService
from .version_service import VersionService from .version_service import VersionService
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -0,0 +1,126 @@
import json
import os
import textwrap
from typing import Optional
from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum
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
class AddService(CommandABC):
def __init__(self, config: ConfigurationABC, workspace: WorkspaceSettings):
"""
Service for CLI command add
"""
CommandABC.__init__(self)
self._config = config
self._workspace = workspace
@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
""")
@staticmethod
def _edit_project_file(source: str, project_settings: ProjectSettings, build_settings: BuildSettings):
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 run(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
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: {" ".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 target in build_settings.project_references:
Console.error(f'Project reference already exists.')
return
if self._workspace is None:
target = f'../{target}'
else:
target = target.replace('src', '..')
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,4 +1,5 @@
from cpl.console.console import Console import textwrap
from cpl_cli.command_abc import CommandABC from cpl_cli.command_abc import CommandABC
from cpl_cli.publish.publisher_abc import PublisherABC from cpl_cli.publish.publisher_abc import PublisherABC
@@ -14,6 +15,13 @@ class BuildService(CommandABC):
self._publisher = publisher 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 run(self, args: list[str]): def run(self, args: list[str]):
""" """
Entry point of command Entry point of command
@@ -21,4 +29,3 @@ class BuildService(CommandABC):
:return: :return:
""" """
self._publisher.build() self._publisher.build()
Console.write('\n')

View File

@@ -1,12 +1,13 @@
import os import os
import textwrap
from collections import Callable from collections import Callable
from cpl.application.application_abc import ApplicationRuntimeABC
from cpl.configuration.configuration_abc import ConfigurationABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.console.console import Console from cpl.console.console import Console
from cpl.utils.string import String from cpl.utils.string import String
from cpl_cli.command_abc import CommandABC from cpl_cli.command_abc import CommandABC
from cpl_cli.templates.generate.init_template import InitTemplate
from cpl_cli.templates.generate.abc_template import ABCTemplate from cpl_cli.templates.generate.abc_template import ABCTemplate
from cpl_cli.templates.generate.class_template import ClassTemplate from cpl_cli.templates.generate.class_template import ClassTemplate
from cpl_cli.templates.generate.configmodel_template import ConfigModelTemplate from cpl_cli.templates.generate.configmodel_template import ConfigModelTemplate
@@ -18,11 +19,10 @@ from cpl_cli.templates.template_file_abc import TemplateFileABC
class GenerateService(CommandABC): class GenerateService(CommandABC):
def __init__(self, configuration: ConfigurationABC, runtime: ApplicationRuntimeABC): def __init__(self, configuration: ConfigurationABC):
""" """
Service for the CLI command generate Service for the CLI command generate
:param configuration: :param configuration:
:param runtime:
""" """
CommandABC.__init__(self) CommandABC.__init__(self)
@@ -54,7 +54,26 @@ class GenerateService(CommandABC):
} }
self._config = configuration self._config = configuration
self._runtime = runtime self._env = self._config.environment
@property
def help_message(self) -> str:
return 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:
abc
class
enum
service
settings
thread
""")
@staticmethod @staticmethod
def _help(message: str): def _help(message: str):
@@ -103,19 +122,35 @@ class GenerateService(CommandABC):
rel_path = '/'.join(parts[:-1]) rel_path = '/'.join(parts[:-1])
class_name = parts[len(parts) - 1] class_name = parts[len(parts) - 1]
if 'src' not in rel_path:
rel_path = f'src/{rel_path}'
template = template(class_name, schematic, self._schematics[schematic]["Upper"], rel_path) template = template(class_name, schematic, self._schematics[schematic]["Upper"], rel_path)
file_path = os.path.join(self._runtime.working_directory, template.path, template.name) file_path = os.path.join(self._env.working_directory, template.path, template.name)
if not os.path.isdir(os.path.dirname(file_path)): if not os.path.isdir(os.path.dirname(file_path)):
os.makedirs(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':
file = InitTemplate(class_name, schematic, self._schematics[schematic]["Upper"], rel_path)
Console.spinner(
f'Creating {os.path.abspath(directory)}/{file.name}',
self._create_file,
os.path.join(os.path.abspath(directory), file.name),
file.value,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
)
if os.path.isfile(file_path): if os.path.isfile(file_path):
Console.error(f'{String.first_to_upper(schematic)} already exists!') Console.error(f'{String.first_to_upper(schematic)} already exists!')
exit() exit()
message = f'Creating {self._runtime.working_directory}/{template.path}/{template.name}' message = f'Creating {self._env.working_directory}/{template.path}/{template.name}'
if template.path == '': if template.path == '':
message = f'Creating {self._runtime.working_directory}/{template.name}' message = f'Creating {self._env.working_directory}/{template.name}'
Console.spinner( Console.spinner(
message, message,
@@ -148,5 +183,3 @@ class GenerateService(CommandABC):
else: else:
self._help('Usage: cpl generate <schematic> [options]') self._help('Usage: cpl generate <schematic> [options]')
exit() exit()
Console.write('\n')

View File

@@ -1,31 +1,66 @@
import textwrap
from typing import Optional
from cpl.console.console import Console from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
from cpl_cli.command_handler_service import CommandHandler
from cpl_cli.command_abc import CommandABC from cpl_cli.command_abc import CommandABC
class HelpService(CommandABC): class HelpService(CommandABC):
def __init__(self): def __init__(self, services: ServiceProviderABC, cmd_handler: CommandHandler):
""" """
Service for CLI command help Service for CLI command help
""" """
CommandABC.__init__(self) CommandABC.__init__(self)
self._services = services
self._commands = cmd_handler.commands
@property
def help_message(self) -> str:
return textwrap.dedent("""\
Lists available command and their short descriptions.
Usage: cpl help <command>
Arguments:
command The command to display the help message for
""")
def run(self, args: list[str]): def run(self, args: list[str]):
""" """
Entry point of command Entry point of command
:param args: :param args:
:return: :return:
""" """
if len(args) > 0:
command_name = args[0]
command: Optional[CommandABC] = None
for cmd in self._commands:
if cmd.name == command_name or command_name in cmd.aliases:
command = self._services.get_service(cmd.command)
if command is None:
Console.error(f'Invalid argument: {command_name}')
return
Console.write_line(command.help_message)
return
Console.write_line('Available Commands:') Console.write_line('Available Commands:')
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.'], ['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.'], ['generate (g|G)', 'Generate a new file.'],
['help (h|H)', 'Lists available command and their short descriptions.'], ['help (h|H)', 'Lists available command and their short descriptions.'],
['install (i|I)', 'With argument installs packages to project, without argument installs project dependencies.'], ['install (i|I)', 'With argument installs packages to project, without argument installs project dependencies.'],
['new (n|N)', 'Creates new CPL project.'], ['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_template.py. Must be executed from within a workspace directory.'], ['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.'],
['start (s|S)', 'Starts CPL project, restarting on file changes'], ['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.'], ['uninstall (ui|UI)', 'Uninstalls packages from project.'],
['update (u|u)', 'Update CPL and project dependencies.'], ['update (u|u)', 'Update CPL and project dependencies.'],
['version (v|V)', 'Outputs CPL CLI version.'] ['version (v|V)', 'Outputs CPL CLI version.']
@@ -35,5 +70,3 @@ class HelpService(CommandABC):
Console.write(f'\n\t{name} ') Console.write(f'\n\t{name} ')
Console.set_foreground_color(ForegroundColorEnum.default) Console.set_foreground_color(ForegroundColorEnum.default)
Console.write(f'{description}') Console.write(f'{description}')
Console.write('\n')

View File

@@ -1,12 +1,14 @@
import json import json
import os import os
import subprocess import subprocess
import textwrap
from packaging import version from packaging import version
from cpl.application.application_runtime_abc import ApplicationRuntimeABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.console import Console from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl.utils.pip import Pip from cpl.utils.pip import Pip
from cpl_cli.cli_settings import CLISettings from cpl_cli.cli_settings import CLISettings
from cpl_cli.command_abc import CommandABC from cpl_cli.command_abc import CommandABC
@@ -18,22 +20,36 @@ from cpl_cli.error import Error
class InstallService(CommandABC): class InstallService(CommandABC):
def __init__(self, runtime: ApplicationRuntimeABC, build_settings: BuildSettings, project_settings: ProjectSettings, def __init__(self, config: ConfigurationABC, env: ApplicationEnvironmentABC, build_settings: BuildSettings,
cli_settings: CLISettings): project_settings: ProjectSettings, cli_settings: CLISettings):
""" """
Service for the CLI command install Service for the CLI command install
:param runtime: :param config:
:param env:
:param build_settings: :param build_settings:
:param project_settings: :param project_settings:
:param cli_settings: :param cli_settings:
""" """
CommandABC.__init__(self) CommandABC.__init__(self)
self._runtime = runtime self._config = config
self._env = env
self._build_settings = build_settings self._build_settings = build_settings
self._project_settings = project_settings self._project_settings = project_settings
self._cli_settings = cli_settings self._cli_settings = cli_settings
self._project_file = f'{self._config.get_configuration("ProjectName")}.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 _install_project(self): def _install_project(self):
""" """
Installs dependencies of CPl project Installs dependencies of CPl project
@@ -45,7 +61,7 @@ class InstallService(CommandABC):
return return
if self._project_settings.dependencies is None: if self._project_settings.dependencies is None:
Error.error('Found invalid dependencies in cpl.json.') Error.error(f'Found invalid dependencies in {self._project_file}.')
return return
Pip.set_executable(self._project_settings.python_executable) Pip.set_executable(self._project_settings.python_executable)
@@ -76,7 +92,7 @@ class InstallService(CommandABC):
return return
if self._project_settings.dependencies is None: if self._project_settings.dependencies is None:
Error.error('Found invalid dependencies in cpl.json.') Error.error(f'Found invalid dependencies in {self._project_file}.')
return return
package_version = '' package_version = ''
@@ -121,6 +137,7 @@ class InstallService(CommandABC):
spinner_foreground_color=ForegroundColorEnum.cyan spinner_foreground_color=ForegroundColorEnum.cyan
) )
new_package = Pip.get_package(name) new_package = Pip.get_package(name)
Console.write_line(new_package)
if new_package is None \ if new_package is None \
or '==' in package and \ or '==' in package and \
version.parse(package.split('==')[1]) != version.parse(new_package.split('==')[1]): version.parse(package.split('==')[1]) != version.parse(new_package.split('==')[1]):
@@ -147,7 +164,7 @@ class InstallService(CommandABC):
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings) BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings)
} }
with open(os.path.join(self._runtime.working_directory, 'cpl.json'), 'w') as project_file: 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.write(json.dumps(config, indent=2))
project_file.close() project_file.close()
@@ -163,5 +180,3 @@ class InstallService(CommandABC):
self._install_project() self._install_project()
else: else:
self._install_package(args[0]) self._install_package(args[0])
Console.write('\n')

View File

@@ -1,44 +1,41 @@
import json
import os import os
import sys import sys
import textwrap
from typing import Optional from typing import Optional
from packaging import version from packaging import version
import cpl import cpl
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
from cpl.configuration.configuration_abc import ConfigurationABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.console.console import Console from cpl.console.console import Console
from cpl.utils.string import String
from cpl_cli.command_abc import CommandABC from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration.build_settings import BuildSettings from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.build_settings_name_enum import BuildSettingsNameEnum from cpl_cli.configuration.build_settings_name_enum import BuildSettingsNameEnum
from cpl_cli.configuration.project_settings import ProjectSettings from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.configuration.project_settings_name_enum import ProjectSettingsNameEnum from cpl_cli.configuration.project_settings_name_enum import ProjectSettingsNameEnum
from cpl_cli.configuration.project_type_enum import ProjectTypeEnum
from cpl_cli.configuration.version_settings_name_enum import VersionSettingsNameEnum from cpl_cli.configuration.version_settings_name_enum import VersionSettingsNameEnum
from cpl_cli.templates.new.console.license import LicenseTemplate from cpl_cli.configuration.workspace_settings import WorkspaceSettings
from cpl_cli.templates.new.console.readme_py import ReadmeTemplate from cpl_cli.source_creator.console_builder import ConsoleBuilder
from cpl_cli.templates.new.console.src.application import ApplicationTemplate from cpl_cli.source_creator.library_builder import LibraryBuilder
from cpl_cli.templates.new.console.src.main import MainWithApplicationHostAndStartupTemplate, MainWithoutApplicationHostTemplate, MainWithApplicationHostTemplate
from cpl_cli.templates.new.console.src.startup import StartupTemplate
from cpl_cli.templates.new.console.src.tests.init import TestsInitTemplate
from cpl_cli.templates.template_file_abc import TemplateFileABC
class NewService(CommandABC): class NewService(CommandABC):
def __init__(self, configuration: ConfigurationABC, runtime: ApplicationRuntimeABC): def __init__(self, configuration: ConfigurationABC):
""" """
Service for the CLI command new Service for the CLI command new
:param configuration: :param configuration:
:param runtime:
""" """
CommandABC.__init__(self) CommandABC.__init__(self)
self._config = configuration self._config = configuration
self._runtime = runtime self._env = self._config.environment
self._workspace = self._config.get_configuration(WorkspaceSettings)
self._project: ProjectSettings = ProjectSettings() self._project: ProjectSettings = ProjectSettings()
self._project_dict = {} self._project_dict = {}
self._build: BuildSettings = BuildSettings() self._build: BuildSettings = BuildSettings()
@@ -50,6 +47,21 @@ class NewService(CommandABC):
self._use_startup: bool = False self._use_startup: bool = False
self._use_service_providing: bool = False self._use_service_providing: 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
library
""")
@staticmethod @staticmethod
def _help(message: str): def _help(message: str):
""" """
@@ -96,10 +108,15 @@ class NewService(CommandABC):
self._project.from_dict(self._project_dict) self._project.from_dict(self._project_dict)
def _create_build_settings(self): def _create_build_settings(self):
main = f'{String.convert_to_snake_case(self._project.name)}.main'
if self._command == ProjectTypeEnum.library.value:
main = f'{String.convert_to_snake_case(self._project.name)}.main'
self._build_dict = { self._build_dict = {
BuildSettingsNameEnum.source_path.value: 'src', BuildSettingsNameEnum.project_type.value: self._command,
BuildSettingsNameEnum.output_path.value: 'dist', BuildSettingsNameEnum.source_path.value: '',
BuildSettingsNameEnum.main.value: 'main', BuildSettingsNameEnum.output_path.value: '../../dist',
BuildSettingsNameEnum.main.value: main,
BuildSettingsNameEnum.entry_point.value: self._project.name, BuildSettingsNameEnum.entry_point.value: self._project.name,
BuildSettingsNameEnum.include_package_data.value: False, BuildSettingsNameEnum.include_package_data.value: False,
BuildSettingsNameEnum.included.value: [], BuildSettingsNameEnum.included.value: [],
@@ -108,7 +125,8 @@ class NewService(CommandABC):
'*/logs', '*/logs',
'*/tests' '*/tests'
], ],
BuildSettingsNameEnum.package_data.value: {} BuildSettingsNameEnum.package_data.value: {},
BuildSettingsNameEnum.project_references.value: []
} }
self._build.from_dict(self._build_dict) self._build.from_dict(self._build_dict)
@@ -127,90 +145,40 @@ class NewService(CommandABC):
Gets project path Gets project path
:return: :return:
""" """
project_path = os.path.join(self._runtime.working_directory, self._project.name) if self._workspace is None:
project_path = os.path.join(self._env.working_directory, self._project.name)
else:
project_path = os.path.join(
self._env.working_directory,
'src',
String.convert_to_snake_case(self._project.name)
)
if os.path.isdir(project_path) and len(os.listdir(project_path)) > 0: if os.path.isdir(project_path) and len(os.listdir(project_path)) > 0:
Console.error('Project path is not empty\n') Console.error('Project path is not empty\n')
return None return None
return project_path return project_path
def _get_project_informations(self): def _get_project_information(self):
""" """
Gets project informations from user Gets project information's from user
:return: :return:
""" """
result = Console.read('Do you want to use application host? (y/n) ') result = Console.read('Do you want to use application base? (y/n) ')
if result.lower() == 'y': if result.lower() == 'y':
self._use_application_api = True self._use_application_api = True
if self._use_application_api:
result = Console.read('Do you want to use startup? (y/n) ') result = Console.read('Do you want to use startup? (y/n) ')
if result.lower() == 'y': if result.lower() == 'y':
self._use_startup = True self._use_startup = True
else:
result = Console.read('Do you want to use service providing? (y/n) ')
if result.lower() == 'y':
self._use_service_providing = True
Console.set_foreground_color(ForegroundColorEnum.default) Console.set_foreground_color(ForegroundColorEnum.default)
# else:
# result = Console.read('Do you want to use service providing? (y/n) ')
# if result.lower() == 'y':
# self._use_service_providing = True
def _build_project_dir(self, project_path: str):
"""
Builds the project files
:param project_path:
:return:
"""
if not os.path.isdir(project_path):
os.makedirs(project_path)
with open(os.path.join(project_path, 'cpl.json'), 'w') as project_json:
project_json.write(json.dumps(self._project_json, indent=2))
project_json.close()
templates: list[TemplateFileABC] = [
LicenseTemplate(),
ReadmeTemplate(),
TestsInitTemplate()
]
if self._use_application_api:
templates.append(ApplicationTemplate())
if self._use_startup:
templates.append(StartupTemplate())
templates.append(MainWithApplicationHostAndStartupTemplate())
else:
templates.append(MainWithApplicationHostTemplate())
else:
templates.append(MainWithoutApplicationHostTemplate())
for template in templates:
Console.spinner(
f'Creating {self._project.name}/{template.path}{template.name}',
self._create_template,
project_path,
template,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
)
@staticmethod
def _create_template(project_path: str, template: TemplateFileABC):
"""
Creates template
:param project_path:
:param template:
:return:
"""
file_path = os.path.join(project_path, template.path, template.name)
file_rel_path = os.path.join(project_path, template.path)
if not os.path.isdir(file_rel_path):
os.makedirs(file_rel_path)
with open(file_path, 'w') as license_file:
license_file.write(template.value)
license_file.close()
def _console(self, args: list[str]): def _console(self, args: list[str]):
""" """
Generates new console project Generates new console project
@@ -226,9 +194,46 @@ class NewService(CommandABC):
if path is None: if path is None:
return return
self._get_project_informations() self._get_project_information()
try: try:
self._build_project_dir(path) ConsoleBuilder.build(
path,
self._use_application_api,
self._use_startup,
self._use_service_providing,
self._project.name,
self._project_json,
self._workspace
)
except Exception as e:
Console.error('Could not create project', str(e))
def _library(self, args: list[str]):
"""
Generates new library project
:param args:
:return:
"""
name = self._config.get_configuration(self._command)
self._create_project_settings(name)
self._create_build_settings()
self._create_project_json()
path = self._get_project_path()
if path is None:
return
self._get_project_information()
try:
LibraryBuilder.build(
path,
self._use_application_api,
self._use_startup,
self._use_service_providing,
self._project.name,
self._project_json,
self._workspace
)
except Exception as e: except Exception as e:
Console.error('Could not create project', str(e)) Console.error('Could not create project', str(e))
@@ -240,14 +245,15 @@ class NewService(CommandABC):
""" """
if len(args) == 0: if len(args) == 0:
self._help('Usage: cpl new <schematic> [options]') self._help('Usage: cpl new <schematic> [options]')
exit() return
self._command = args[0] self._command = str(args[0]).lower()
if self._command == 'console': if self._command == ProjectTypeEnum.console.value:
self._console(args) self._console(args)
elif self._command == ProjectTypeEnum.library.value:
self._library(args)
else: else:
self._help('Usage: cpl new <schematic> [options]') self._help('Usage: cpl new <schematic> [options]')
exit() return
Console.write('\n')

View File

@@ -1,4 +1,5 @@
from cpl.console.console import Console import textwrap
from cpl_cli.command_abc import CommandABC from cpl_cli.command_abc import CommandABC
from cpl_cli.publish.publisher_abc import PublisherABC from cpl_cli.publish.publisher_abc import PublisherABC
@@ -14,6 +15,13 @@ class PublishService(CommandABC):
self._publisher = publisher 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 run(self, args: list[str]): def run(self, args: list[str]):
""" """
Entry point of command Entry point of command
@@ -21,4 +29,3 @@ class PublishService(CommandABC):
:return: :return:
""" """
self._publisher.publish() self._publisher.publish()
Console.write('\n')

View File

@@ -0,0 +1,99 @@
import os
import shutil
import json
import textwrap
from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration import WorkspaceSettings, WorkspaceSettingsNameEnum
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)
@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
""")
@staticmethod
def _create_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()
@staticmethod
def _remove_sources(path: str):
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
}
}
self._create_file(path, ws_dict)
def run(self, args: list[str]):
"""
Entry point of command
:param args:
:return:
"""
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.abspath(os.path.dirname(self._workspace.projects[project_name]))
Console.spinner(
f'Removing {src_path}',
self._remove_sources,
src_path,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
)
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,3 +1,5 @@
import textwrap
from cpl_cli.command_abc import CommandABC from cpl_cli.command_abc import CommandABC
from cpl_cli.live_server.live_server_service import LiveServerService from cpl_cli.live_server.live_server_service import LiveServerService
@@ -13,10 +15,17 @@ class StartService(CommandABC):
self._live_server = live_server 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 run(self, args: list[str]): def run(self, args: list[str]):
""" """
Entry point of command Entry point of command
:param args: :param args:
:return: :return:
""" """
self._live_server.start() self._live_server.start(args)

View File

@@ -1,10 +1,12 @@
import json import json
import os import os
import subprocess import subprocess
import textwrap
from cpl.application.application_runtime_abc import ApplicationRuntimeABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.console import Console from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl.utils.pip import Pip from cpl.utils.pip import Pip
from cpl_cli.command_abc import CommandABC from cpl_cli.command_abc import CommandABC
from cpl_cli.configuration.build_settings import BuildSettings from cpl_cli.configuration.build_settings import BuildSettings
@@ -14,21 +16,32 @@ from cpl_cli.configuration.settings_helper import SettingsHelper
class UninstallService(CommandABC): class UninstallService(CommandABC):
def __init__(self, runtime: ApplicationRuntimeABC, build_settings: BuildSettings, def __init__(self, config: ConfigurationABC, env: ApplicationEnvironmentABC, build_settings: BuildSettings,
project_settings: ProjectSettings): project_settings: ProjectSettings):
""" """
Service for the CLI command uninstall Service for the CLI command uninstall
:param runtime: :param config:
:param env:
:param build_settings: :param build_settings:
:param project_settings: :param project_settings:
""" """
CommandABC.__init__(self) CommandABC.__init__(self)
self._runtime = runtime self._config = config
self._env = env
self._build_settings = build_settings self._build_settings = build_settings
self._project_settings = project_settings self._project_settings = project_settings
@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 run(self, args: list[str]): def run(self, args: list[str]):
""" """
Entry point of command Entry point of command
@@ -74,7 +87,7 @@ class UninstallService(CommandABC):
ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(self._project_settings), ProjectSettings.__name__: SettingsHelper.get_project_settings_dict(self._project_settings),
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings) BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings)
} }
with open(os.path.join(self._runtime.working_directory, 'cpl.json'), 'w') as project_file: with open(os.path.join(self._env.working_directory, f'{self._config.get_configuration("ProjectName")}.json'), 'w') as project_file:
project_file.write(json.dumps(config, indent=2)) project_file.write(json.dumps(config, indent=2))
project_file.close() project_file.close()

View File

@@ -1,10 +1,12 @@
import json import json
import os import os
import subprocess import subprocess
import textwrap
from cpl.application.application_runtime_abc import ApplicationRuntimeABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.console import Console from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl.utils.pip import Pip from cpl.utils.pip import Pip
from cpl_cli.cli_settings import CLISettings from cpl_cli.cli_settings import CLISettings
from cpl_cli.command_abc import CommandABC from cpl_cli.command_abc import CommandABC
@@ -16,34 +18,55 @@ from cpl_cli.configuration.settings_helper import SettingsHelper
class UpdateService(CommandABC): class UpdateService(CommandABC):
def __init__(self, def __init__(self,
runtime: ApplicationRuntimeABC, config: ConfigurationABC,
env: ApplicationEnvironmentABC,
build_settings: BuildSettings, build_settings: BuildSettings,
project_settings: ProjectSettings, project_settings: ProjectSettings,
cli_settings: CLISettings): cli_settings: CLISettings):
""" """
Service for the CLI command update Service for the CLI command update
:param runtime: :param config:
:param env:
:param build_settings: :param build_settings:
:param project_settings: :param project_settings:
:param cli_settings: :param cli_settings:
""" """
CommandABC.__init__(self) CommandABC.__init__(self)
self._runtime = runtime self._config = config
self._env = env
self._build_settings = build_settings self._build_settings = build_settings
self._project_settings = project_settings self._project_settings = project_settings
self._cli_settings = cli_settings self._cli_settings = cli_settings
def _update_project_dependencies(self): @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]:
""" """
Updates project dependencies Collects project dependencies
:return: :return:
""" """
dependencies = []
for package in self._project_settings.dependencies: for package in self._project_settings.dependencies:
name = package name = package
if '==' in package: if '==' in package:
name = package.split('==')[0] 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( Pip.install(
name, name,
'--upgrade', '--upgrade',
@@ -66,11 +89,18 @@ class UpdateService(CommandABC):
Checks project dependencies for updates Checks project dependencies for updates
:return: :return:
""" """
Console.spinner( dependencies = Console.spinner(
'Collecting installed dependencies', self._update_project_dependencies, 'Collecting installed dependencies', self._collect_project_dependencies,
text_foreground_color=ForegroundColorEnum.green, text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan 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
)
Console.write_line(f'Found {len(self._project_settings.dependencies)} dependencies.') Console.write_line(f'Found {len(self._project_settings.dependencies)} dependencies.')
@staticmethod @staticmethod
@@ -97,7 +127,7 @@ class UpdateService(CommandABC):
def _project_json_update_dependency(self, old_package: str, new_package: str): def _project_json_update_dependency(self, old_package: str, new_package: str):
""" """
Writes new package version to cpl.json Writes new package version to project.json
:param old_package: :param old_package:
:param new_package: :param new_package:
:return: :return:
@@ -117,7 +147,8 @@ class UpdateService(CommandABC):
BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings) BuildSettings.__name__: SettingsHelper.get_build_settings_dict(self._build_settings)
} }
with open(os.path.join(self._runtime.working_directory, 'cpl.json'), 'w') as project: with open(os.path.join(self._env.working_directory, f'{self._config.get_configuration("ProjectName")}.json'),
'w') as project:
project.write(json.dumps(config, indent=2)) project.write(json.dumps(config, indent=2))
project.close() project.close()
@@ -131,5 +162,3 @@ class UpdateService(CommandABC):
self._check_project_dependencies() self._check_project_dependencies()
self._check_outdated() self._check_outdated()
Pip.reset_executable() Pip.reset_executable()
Console.write('\n')

View File

@@ -2,6 +2,7 @@ import pkgutil
import sys import sys
import platform import platform
import pkg_resources import pkg_resources
import textwrap
import cpl import cpl
import cpl_cli import cpl_cli
@@ -18,6 +19,13 @@ class VersionService(CommandABC):
""" """
CommandABC.__init__(self) 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 run(self, args: list[str]): def run(self, args: list[str]):
""" """
Entry point of command Entry point of command

View File

@@ -1,13 +1,15 @@
from abc import abstractmethod from abc import abstractmethod, ABC
from cpl.dependency_injection.service_abc import ServiceABC
class CommandABC(ServiceABC): class CommandABC(ABC):
@abstractmethod @abstractmethod
def __init__(self): def __init__(self):
ServiceABC.__init__(self) ABC.__init__(self)
@property
@abstractmethod
def help_message(self) -> str: pass
@abstractmethod @abstractmethod
def run(self, args: list[str]): pass def run(self, args: list[str]): pass

View File

@@ -1,25 +1,28 @@
import os import os
from abc import ABC
from typing import Optional
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
from cpl.configuration.configuration_abc import ConfigurationABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.dependency_injection.service_abc import ServiceABC from cpl.console.console import Console
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
from cpl.utils.string import String
from cpl_cli.configuration.workspace_settings import WorkspaceSettings
from cpl_cli.error import Error from cpl_cli.error import Error
from cpl_cli.command_model import CommandModel from cpl_cli.command_model import CommandModel
class CommandHandler(ServiceABC): class CommandHandler(ABC):
def __init__(self, runtime: ApplicationRuntimeABC, config: ConfigurationABC, services: ServiceProviderABC): def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
""" """
Service to handle incoming commands and args Service to handle incoming commands and args
:param runtime: :param config:
:param services: :param services:
""" """
ServiceABC.__init__(self) ABC.__init__(self)
self._runtime = runtime
self._config = config self._config = config
self._env = self._config.environment
self._services = services self._services = services
self._commands: list[CommandModel] = [] self._commands: list[CommandModel] = []
@@ -28,6 +31,12 @@ class CommandHandler(ServiceABC):
def commands(self) -> list[CommandModel]: def commands(self) -> list[CommandModel]:
return self._commands return self._commands
@staticmethod
def _project_not_found():
Error.error(
'The command requires to be run in an CPL project, but a project could not be found.'
)
def add_command(self, cmd: CommandModel): def add_command(self, cmd: CommandModel):
self._commands.append(cmd) self._commands.append(cmd)
@@ -43,11 +52,63 @@ class CommandHandler(ServiceABC):
""" """
for command in self._commands: for command in self._commands:
if cmd == command.name or cmd in command.aliases: if cmd == command.name or cmd in command.aliases:
if command.is_project_needed and not os.path.isfile(os.path.join(self._runtime.working_directory, 'cpl.json')): error = None
Error.error('The command requires to be run in an CPL project, but a project could not be found.') project_name: Optional[str] = None
return workspace: Optional[WorkspaceSettings] = None
if os.path.isfile(os.path.join(self._env.working_directory, 'cpl-workspace.json')):
self._config.add_json_file('cpl-workspace.json', optional=True, output=False)
workspace = self._config.get_configuration(WorkspaceSettings)
if command.is_project_needed: if command.is_project_needed:
self._config.add_json_file('cpl.json', optional=True, output=False) name = os.path.basename(self._env.working_directory)
for r, d, f in os.walk(self._env.working_directory):
for file in f:
if file.endswith('.json'):
f_name = file.split('.json')[0]
if f_name == name or \
String.convert_to_camel_case(f_name) == String.convert_to_camel_case(name):
project_name = f_name
break
if not command.is_workspace_needed and project_name is None and workspace is None:
self._project_not_found()
return
elif command.is_workspace_needed or project_name is None:
if workspace is None:
Error.error(
'The command requires to be run in an CPL workspace or project, '
'but a workspace or project could not be found.'
)
return
if project_name is None:
project_name = workspace.default_project
self._config.add_configuration('ProjectName', project_name)
project_json = f'{project_name}.json'
if workspace is not None:
if project_name not in workspace.projects:
Error.error(
f'Project {project_name} not found.'
)
return
project_json = workspace.projects[project_name]
if not os.path.isfile(os.path.join(self._env.working_directory, project_json)):
self._project_not_found()
return
project_json = os.path.join(self._env.working_directory, project_json)
if command.change_cwd:
self._env.set_working_directory(
os.path.join(self._env.working_directory, os.path.dirname(project_json))
)
self._config.add_json_file(project_json, optional=True, output=False)
self._services.get_service(command.command).run(args) self._services.get_service(command.command).run(args)
Console.write('\n')

View File

@@ -5,11 +5,14 @@ from cpl_cli.command_abc import CommandABC
class CommandModel: class CommandModel:
def __init__(self, name: str, aliases: list[str], command: Callable[CommandABC], is_project_needed: bool): def __init__(self, name: str, aliases: list[str], command: Callable[CommandABC], is_workspace_needed: bool,
is_project_needed: bool, change_cwd: bool):
self._name = name self._name = name
self._aliases = aliases self._aliases = aliases
self._command = command self._command = command
self._is_workspace_needed = is_workspace_needed
self._is_project_needed = is_project_needed self._is_project_needed = is_project_needed
self._change_cwd = change_cwd
@property @property
def name(self) -> str: def name(self) -> str:
@@ -23,6 +26,14 @@ class CommandModel:
def command(self) -> Callable[CommandABC]: def command(self) -> Callable[CommandABC]:
return self._command return self._command
@property
def is_workspace_needed(self) -> bool:
return self._is_workspace_needed
@property @property
def is_project_needed(self) -> bool: def is_project_needed(self) -> bool:
return self._is_project_needed return self._is_project_needed
@property
def change_cwd(self) -> bool:
return self._change_cwd

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
@@ -15,7 +15,7 @@ __title__ = 'cpl_cli.configuration'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
@@ -26,6 +26,8 @@ from .project_settings import ProjectSettings
from .project_settings_name_enum import ProjectSettingsNameEnum from .project_settings_name_enum import ProjectSettingsNameEnum
from .version_settings import VersionSettings from .version_settings import VersionSettings
from .version_settings_name_enum import VersionSettingsNameEnum 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') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -1,3 +1,4 @@
import sys
import traceback import traceback
from typing import Optional from typing import Optional
@@ -5,6 +6,7 @@ from cpl.configuration.configuration_model_abc import ConfigurationModelABC
from cpl.console.console import Console from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl_cli.configuration.build_settings_name_enum import BuildSettingsNameEnum from cpl_cli.configuration.build_settings_name_enum import BuildSettingsNameEnum
from cpl_cli.configuration.project_type_enum import ProjectTypeEnum
class BuildSettings(ConfigurationModelABC): class BuildSettings(ConfigurationModelABC):
@@ -12,6 +14,7 @@ class BuildSettings(ConfigurationModelABC):
def __init__(self): def __init__(self):
ConfigurationModelABC.__init__(self) ConfigurationModelABC.__init__(self)
self._project_type: Optional[ProjectTypeEnum] = None
self._source_path: Optional[str] = None self._source_path: Optional[str] = None
self._output_path: Optional[str] = None self._output_path: Optional[str] = None
self._main: Optional[str] = None self._main: Optional[str] = None
@@ -20,6 +23,11 @@ class BuildSettings(ConfigurationModelABC):
self._included: Optional[list[str]] = None self._included: Optional[list[str]] = None
self._excluded: Optional[list[str]] = None self._excluded: Optional[list[str]] = None
self._package_data: Optional[dict[str, list[str]]] = None self._package_data: Optional[dict[str, list[str]]] = None
self._project_references: Optional[list[str]] = None
@property
def project_type(self):
return self._project_type
@property @property
def source_path(self) -> str: def source_path(self) -> str:
@@ -53,8 +61,13 @@ class BuildSettings(ConfigurationModelABC):
def package_data(self) -> dict[str, list[str]]: def package_data(self) -> dict[str, list[str]]:
return self._package_data return self._package_data
@property
def project_references(self) -> list[str]:
return self._project_references
def from_dict(self, settings: dict): def from_dict(self, settings: dict):
try: try:
self._project_type = settings[BuildSettingsNameEnum.project_type.value]
self._source_path = settings[BuildSettingsNameEnum.source_path.value] self._source_path = settings[BuildSettingsNameEnum.source_path.value]
self._output_path = settings[BuildSettingsNameEnum.output_path.value] self._output_path = settings[BuildSettingsNameEnum.output_path.value]
self._include_package_data = bool(settings[BuildSettingsNameEnum.include_package_data.value]) self._include_package_data = bool(settings[BuildSettingsNameEnum.include_package_data.value])
@@ -63,6 +76,26 @@ class BuildSettings(ConfigurationModelABC):
self._included = settings[BuildSettingsNameEnum.included.value] self._included = settings[BuildSettingsNameEnum.included.value]
self._excluded = settings[BuildSettingsNameEnum.excluded.value] self._excluded = settings[BuildSettingsNameEnum.excluded.value]
self._package_data = settings[BuildSettingsNameEnum.package_data.value] self._package_data = settings[BuildSettingsNameEnum.package_data.value]
self._project_references = settings[BuildSettingsNameEnum.project_references.value]
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
except Exception as e: except Exception as e:
Console.set_foreground_color(ForegroundColorEnum.red) Console.set_foreground_color(ForegroundColorEnum.red)
Console.write_line( Console.write_line(

View File

@@ -3,6 +3,7 @@ from enum import Enum
class BuildSettingsNameEnum(Enum): class BuildSettingsNameEnum(Enum):
project_type = 'ProjectType'
source_path = 'SourcePath' source_path = 'SourcePath'
output_path = 'OutputPath' output_path = 'OutputPath'
main = 'Main' main = 'Main'
@@ -11,3 +12,4 @@ class BuildSettingsNameEnum(Enum):
included = 'Included' included = 'Included'
excluded = 'Excluded' excluded = 'Excluded'
package_data = 'PackageData' package_data = 'PackageData'
project_references = 'ProjectReferences'

View File

@@ -0,0 +1,7 @@
from enum import Enum
class ProjectTypeEnum(Enum):
console = 'console'
library = 'library'

View File

@@ -34,6 +34,7 @@ class SettingsHelper:
@staticmethod @staticmethod
def get_build_settings_dict(build: BuildSettings) -> dict: def get_build_settings_dict(build: BuildSettings) -> dict:
return { return {
BuildSettingsNameEnum.project_type.value: build.project_type,
BuildSettingsNameEnum.source_path.value: build.source_path, BuildSettingsNameEnum.source_path.value: build.source_path,
BuildSettingsNameEnum.output_path.value: build.output_path, BuildSettingsNameEnum.output_path.value: build.output_path,
BuildSettingsNameEnum.main.value: build.main, BuildSettingsNameEnum.main.value: build.main,
@@ -41,5 +42,6 @@ class SettingsHelper:
BuildSettingsNameEnum.include_package_data.value: build.include_package_data, BuildSettingsNameEnum.include_package_data.value: build.include_package_data,
BuildSettingsNameEnum.included.value: build.included, BuildSettingsNameEnum.included.value: build.included,
BuildSettingsNameEnum.excluded.value: build.excluded, BuildSettingsNameEnum.excluded.value: build.excluded,
BuildSettingsNameEnum.package_data.value: build.package_data BuildSettingsNameEnum.package_data.value: build.package_data,
BuildSettingsNameEnum.project_references.value: build.project_references
} }

View File

@@ -31,16 +31,25 @@ class VersionSettings(ConfigurationModelABC):
return self._micro return self._micro
def to_str(self) -> str: def to_str(self) -> str:
return f'{self._major}.{self._minor}.{self._micro}' if self._micro is None:
return f'{self._major}.{self._minor}'
else:
return f'{self._major}.{self._minor}.{self._micro}'
def from_dict(self, settings: dict): def from_dict(self, settings: dict):
self._major = settings[VersionSettingsNameEnum.major.value] self._major = settings[VersionSettingsNameEnum.major.value]
self._minor = settings[VersionSettingsNameEnum.minor.value] self._minor = settings[VersionSettingsNameEnum.minor.value]
self._micro = settings[VersionSettingsNameEnum.micro.value] micro = settings[VersionSettingsNameEnum.micro.value]
if micro != '':
self._micro = micro
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { version = {
VersionSettingsNameEnum.major.value: self._major, VersionSettingsNameEnum.major.value: self._major,
VersionSettingsNameEnum.minor.value: self._minor, VersionSettingsNameEnum.minor.value: self._minor,
VersionSettingsNameEnum.micro.value: self._micro
} }
if self._micro is not None:
version[VersionSettingsNameEnum.micro.value] = self._micro
return version

View File

@@ -0,0 +1,31 @@
import traceback
from typing import Optional
from cpl.configuration.configuration_model_abc import ConfigurationModelABC
from cpl.console import Console
from cpl_cli.configuration.workspace_settings_name_enum import WorkspaceSettingsNameEnum
class WorkspaceSettings(ConfigurationModelABC):
def __init__(self):
ConfigurationModelABC.__init__(self)
self._default_project: Optional[str] = None
self._projects: dict[str, str] = {}
@property
def default_project(self) -> str:
return self._default_project
@property
def projects(self) -> dict[str, str]:
return self._projects
def from_dict(self, settings: dict):
try:
self._default_project = settings[WorkspaceSettingsNameEnum.default_project.value]
self._projects = settings[WorkspaceSettingsNameEnum.projects.value]
except Exception as e:
Console.error(f'[ ERROR ] [ {__name__} ]: Reading error in {self.__name__} settings')
Console.error(f'[ EXCEPTION ] [ {__name__} ]: {e} -> {traceback.format_exc()}')

View File

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

47
src/cpl_cli/cpl_cli.json Normal file
View File

@@ -0,0 +1,47 @@
{
"ProjectSettings": {
"Name": "sh_cpl-cli",
"Version": {
"Major": "2021",
"Minor": "4",
"Micro": ""
},
"Author": "Sven Heidemann",
"AuthorEmail": "sven.heidemann@sh-edraft.de",
"Description": "sh-edraft Common Python library CLI",
"LongDescription": "sh-edraft Common Python library Command Line Interface",
"URL": "https://www.sh-edraft.de",
"CopyrightDate": "2020 - 2021",
"CopyrightName": "sh-edraft.de",
"LicenseName": "MIT",
"LicenseDescription": "MIT, see LICENSE for more details.",
"Dependencies": [
"sh_cpl==2021.4.post1"
],
"PythonVersion": ">=3.8",
"PythonPath": {},
"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"
]
},
"ProjectReferences": []
}
}

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
@@ -15,11 +15,11 @@ __title__ = 'cpl_cli.live_server'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -6,31 +6,35 @@ import psutil as psutil
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer from watchdog.observers import Observer
from cpl.application.application_runtime_abc import ApplicationRuntimeABC
from cpl.console.console import Console from cpl.console.console import Console
from cpl.dependency_injection.service_abc import ServiceABC from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_cli.configuration.build_settings import BuildSettings 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_cli.live_server.live_server_thread import LiveServerThread
class LiveServerService(ServiceABC, FileSystemEventHandler): class LiveServerService(FileSystemEventHandler):
def __init__(self, runtime: ApplicationRuntimeABC, build_settings: BuildSettings): def __init__(self, env: ApplicationEnvironmentABC, project_settings: ProjectSettings,
build_settings: BuildSettings):
""" """
Service for the live development server Service for the live development server
:param runtime: :param env:
:param project_settings:
:param build_settings: :param build_settings:
""" """
ServiceABC.__init__(self)
FileSystemEventHandler.__init__(self) FileSystemEventHandler.__init__(self)
self._runtime = runtime self._env = env
self._project_settings = project_settings
self._build_settings = build_settings self._build_settings = build_settings
self._src_dir = os.path.join(self._runtime.working_directory, self._build_settings.source_path) self._src_dir = os.path.join(self._env.working_directory, self._build_settings.source_path)
self._ls_thread = None self._ls_thread = None
self._observer = None self._observer = None
self._args: list[str] = []
def _start_observer(self): def _start_observer(self):
""" """
Starts the file changes observer Starts the file changes observer
@@ -72,15 +76,27 @@ class LiveServerService(ServiceABC, FileSystemEventHandler):
def _start(self): def _start(self):
self._start_observer() self._start_observer()
self._ls_thread = LiveServerThread(self._src_dir) self._ls_thread = LiveServerThread(
self._project_settings.python_executable,
self._src_dir,
self._args,
self._env,
self._build_settings
)
self._ls_thread.start() self._ls_thread.start()
self._ls_thread.join() self._ls_thread.join()
Console.close() Console.close()
def start(self): def start(self, args: list[str]):
""" """
Starts the CPL live development server Starts the CPL live development server
:param args:
:return: :return:
""" """
if self._build_settings.main == '':
Console.error('Project has no entry point.')
return
self._args = args
Console.write_line('** CPL live development server is running **') Console.write_line('** CPL live development server is running **')
self._start() self._start()

View File

@@ -6,20 +6,36 @@ from datetime import datetime
from cpl.console.console import Console from cpl.console.console import Console
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_cli.configuration import BuildSettings
class LiveServerThread(threading.Thread): class LiveServerThread(threading.Thread):
def __init__(self, path: str): 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 Thread to start the CPL project for the live development server
:param executable:
:param path: :param path:
:param args:
:param env:
:param build_settings:
""" """
threading.Thread.__init__(self) threading.Thread.__init__(self)
self._executable = os.path.abspath(executable)
self._path = path self._path = path
self._args = args
self._env = env
self._build_settings = build_settings
self._main = '' self._main = ''
self._command = [] self._command = []
self._env_vars = os.environ
self._set_venv()
@property @property
def command(self) -> list[str]: def command(self) -> list[str]:
@@ -29,16 +45,38 @@ class LiveServerThread(threading.Thread):
def main(self) -> str: def main(self) -> str:
return self._main return self._main
def _set_venv(self):
if self._executable != sys.executable:
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): def run(self):
""" """
Starts the CPL project Starts the CPL project
:return: :return:
""" """
self._main = os.path.join(self._path, 'main.py') 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): if not os.path.isfile(self._main):
Console.error('Entry point main.py not found') Console.error('Entry point main.py not found')
return return
if sys.platform == 'win32':
self._env_vars['PYTHONPATH'] = f'{self._env.working_directory};' \
f'{os.path.join(self._env.working_directory, self._build_settings.source_path)}'
else:
self._env_vars['PYTHONPATH'] = f'{self._env.working_directory}:' \
f'{os.path.join(self._env.working_directory, self._build_settings.source_path)}'
Console.set_foreground_color(ForegroundColorEnum.green) Console.set_foreground_color(ForegroundColorEnum.green)
Console.write_line('Read successfully') Console.write_line('Read successfully')
Console.set_foreground_color(ForegroundColorEnum.cyan) Console.set_foreground_color(ForegroundColorEnum.cyan)
@@ -46,5 +84,6 @@ class LiveServerThread(threading.Thread):
Console.write_line(f'Started at {now.strftime("%Y-%m-%d %H:%M:%S")}\n\n') Console.write_line(f'Started at {now.strftime("%Y-%m-%d %H:%M:%S")}\n\n')
Console.set_foreground_color(ForegroundColorEnum.default) Console.set_foreground_color(ForegroundColorEnum.default)
self._command = [sys.executable, self._main, ''.join(sys.argv[2:])] os.chdir(self._env.working_directory)
subprocess.run(self._command) self._command = [self._executable, self._main, ''.join(self._args)]
subprocess.run(self._command, env=self._env_vars)

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
@@ -15,7 +15,7 @@ __title__ = 'cpl_cli.publish'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
@@ -24,4 +24,4 @@ from .publisher_abc import PublisherABC
from .publisher_service import PublisherService from .publisher_service import PublisherService
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -1,13 +1,11 @@
from abc import abstractmethod from abc import abstractmethod, ABC
from cpl.dependency_injection.service_abc import ServiceABC
class PublisherABC(ServiceABC): class PublisherABC(ABC):
@abstractmethod @abstractmethod
def __init__(self): def __init__(self):
ServiceABC.__init__(self) ABC.__init__(self)
@property @property
@abstractmethod @abstractmethod

View File

@@ -1,15 +1,16 @@
import importlib
import os import os
import shutil import shutil
import sys
from string import Template as stringTemplate from string import Template as stringTemplate
import setuptools import setuptools
from packaging import version from packaging import version
from setuptools import sandbox from setuptools import sandbox
from cpl.application.application_runtime_abc import ApplicationRuntimeABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.console.foreground_color_enum import ForegroundColorEnum from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.console.console import Console from cpl.console.console import Console
from cpl.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl_cli.configuration.build_settings import BuildSettings from cpl_cli.configuration.build_settings import BuildSettings
from cpl_cli.configuration.project_settings import ProjectSettings from cpl_cli.configuration.project_settings import ProjectSettings
from cpl_cli.publish.publisher_abc import PublisherABC from cpl_cli.publish.publisher_abc import PublisherABC
@@ -19,45 +20,53 @@ from cpl_cli.templates.publish.setup_template import SetupTemplate
class PublisherService(PublisherABC): class PublisherService(PublisherABC):
def __init__(self, runtime: ApplicationRuntimeABC, project: ProjectSettings, build: BuildSettings): def __init__(self, config: ConfigurationABC,
env: ApplicationEnvironmentABC, project: ProjectSettings, build: BuildSettings):
""" """
Service to build or publish files for distribution Service to build or publish files for distribution
:param runtime: :param config:
:param env:
:param project: :param project:
:param build: :param build:
""" """
PublisherABC.__init__(self) PublisherABC.__init__(self)
self._runtime = runtime self._config = config
self._env = env
self._project_settings = project self._project_settings = project
self._build_settings = build self._build_settings = build
self._source_path = os.path.join(self._runtime.working_directory, self._build_settings.source_path) self._source_path = os.path.join(self._env.working_directory, self._build_settings.source_path)
self._output_path = os.path.join(self._runtime.working_directory, self._build_settings.output_path) self._output_path = os.path.join(self._env.working_directory, self._build_settings.output_path)
self._included_files: list[str] = [] self._included_files: list[str] = []
self._included_dirs: list[str] = [] self._included_dirs: list[str] = []
self._distributed_files: 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 @property
def source_path(self) -> str: def source_path(self) -> str:
return '' return self._source_path
@property @property
def dist_path(self) -> str: def dist_path(self) -> str:
return '' return self._output_path
@staticmethod def _get_module_name_from_dirs(self, file: str) -> str:
def _get_module_name_from_dirs(file: str) -> str:
""" """
Extracts module name from directories Extracts module name from directories
:param file: :param file:
:return: :return:
""" """
if 'src/' in file: if self._src_path_part in file:
file = file.replace('src/', '', 1) file = file.replace(self._src_path_part, '', 1)
dirs = os.path.dirname(file).split('/') dirs = os.path.dirname(file).split(self._path_mark)
for d in dirs: for d in dirs:
if d.__contains__('.'): if d.__contains__('.'):
dirs.remove(d) dirs.remove(d)
@@ -125,6 +134,43 @@ class PublisherService(PublisherABC):
return False 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): def _read_sources(self):
""" """
Reads all source files and save included files Reads all source files and save included files
@@ -138,28 +184,21 @@ class PublisherService(PublisherABC):
relative_path = os.path.relpath(r) relative_path = os.path.relpath(r)
file_path = os.path.join(relative_path, os.path.relpath(sub_file)) file_path = os.path.join(relative_path, os.path.relpath(sub_file))
print(file_path)
self._included_files.append(os.path.relpath(file_path)) self._included_files.append(os.path.relpath(file_path))
elif os.path.isfile(rel_path): elif os.path.isfile(rel_path):
print(rel_path)
self._included_files.append(rel_path) self._included_files.append(rel_path)
for r, d, f in os.walk(self._build_settings.source_path): self._read_sources_from_path(self._source_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_path_excluded(relative_path):
break
if len(d) > 0: for project in self._build_settings.project_references:
for directory in d: project = os.path.abspath(os.path.join(self._source_path, project))
empty_dir = os.path.join(os.path.dirname(file_path), directory) if not os.path.isfile(os.path.abspath(project)):
if len(os.listdir(empty_dir)) == 0: Console.error(f'Cannot import project: {project}')
self._included_dirs.append(empty_dir) return
if not self._is_path_excluded(relative_path): self.exclude(f'*/{os.path.basename(project)}')
self._included_files.append(os.path.relpath(file_path)) self._read_sources_from_path(os.path.dirname(project))
def _create_packages(self): def _create_packages(self):
""" """
@@ -174,8 +213,6 @@ class PublisherService(PublisherABC):
title = self._get_module_name_from_dirs(file) title = self._get_module_name_from_dirs(file)
if title == '': if title == '':
title = self._project_settings.name title = self._project_settings.name
elif not title.__contains__('.'):
title = f'{self._project_settings.name}.{title}'
module_py_lines: list[str] = [] module_py_lines: list[str] = []
imports = '' imports = ''
@@ -230,8 +267,8 @@ class PublisherService(PublisherABC):
for file in self._included_files: for file in self._included_files:
dist_file = file dist_file = file
if 'src/' in dist_file: if self._src_path_part in dist_file:
dist_file = dist_file.replace('src/', '', 1) dist_file = dist_file.replace(self._src_path_part, '', 1)
output_path = os.path.join(build_path, os.path.dirname(dist_file)) output_path = os.path.join(build_path, os.path.dirname(dist_file))
output_file = os.path.join(build_path, dist_file) output_file = os.path.join(build_path, dist_file)
@@ -252,8 +289,8 @@ class PublisherService(PublisherABC):
for empty_dir in self._included_dirs: for empty_dir in self._included_dirs:
dist_dir = empty_dir dist_dir = empty_dir
if 'src/' in dist_dir: if self._src_path_part in dist_dir:
dist_dir = dist_dir.replace('src/', '', 1) dist_dir = dist_dir.replace(self._src_path_part, '', 1)
output_path = os.path.join(build_path, dist_dir) output_path = os.path.join(build_path, dist_dir)
if not os.path.isdir(output_path): if not os.path.isdir(output_path):
@@ -272,7 +309,7 @@ class PublisherService(PublisherABC):
os.remove(file) os.remove(file)
for path in paths: for path in paths:
if os.path.isdir(path): if path != self._output_path and os.path.isdir(path):
shutil.rmtree(path) shutil.rmtree(path)
def _create_setup(self): def _create_setup(self):
@@ -286,15 +323,39 @@ class PublisherService(PublisherABC):
if os.path.isfile(setup_file): if os.path.isfile(setup_file):
os.remove(setup_file) os.remove(setup_file)
main = None entry_points = {}
try: if self._build_settings.main != "":
main = importlib.import_module(self._build_settings.main) main = None
except Exception as e: try:
Console.error('Could not find entry point', str(e)) main_name = self._build_settings.main
if main is None or not hasattr(main, 'main'): if '.' in self._build_settings.main:
Console.error('Could not find entry point') length = len(self._build_settings.main.split('.'))
return main_name = self._build_settings.main.split('.')[length - 1]
sys.path.insert(0, 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: with open(setup_file, 'w+') as setup_py:
setup_string = stringTemplate(SetupTemplate.get_setup_py()).substitute( setup_string = stringTemplate(SetupTemplate.get_setup_py()).substitute(
@@ -309,11 +370,7 @@ class PublisherService(PublisherABC):
Description=self._project_settings.description, Description=self._project_settings.description,
PyRequires=self._project_settings.python_version, PyRequires=self._project_settings.python_version,
Dependencies=self._project_settings.dependencies, Dependencies=self._project_settings.dependencies,
EntryPoints={ EntryPoints=entry_points,
'console_scripts': [
f'{self._build_settings.entry_point} = {main.__name__}:{main.main.__name__}'
]
},
PackageData=self._build_settings.package_data PackageData=self._build_settings.package_data
) )
setup_py.write(setup_string) setup_py.write(setup_string)
@@ -366,11 +423,17 @@ class PublisherService(PublisherABC):
3. Copies all included source files to dist_path/build 3. Copies all included source files to dist_path/build
:return: :return:
""" """
self._output_path = os.path.join(self._output_path, 'build') self._env.set_working_directory(os.path.join(self._env.working_directory, '../'))
self.exclude(f'*/{self._config.get_configuration("ProjectName")}.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('Reading source files:', self._read_sources, text_foreground_color=ForegroundColorEnum.green,
Console.spinner('Creating internal packages:', self._create_packages, text_foreground_color=ForegroundColorEnum.green, spinner_foreground_color=ForegroundColorEnum.blue) spinner_foreground_color=ForegroundColorEnum.blue)
Console.spinner('Building application:', self._dist_files, 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)
def publish(self): def publish(self):
""" """
@@ -382,15 +445,45 @@ class PublisherService(PublisherABC):
4. Remove all included source from dist_path/publish 4. Remove all included source from dist_path/publish
:return: :return:
""" """
self._output_path = os.path.join(self._output_path, 'publish') self._env.set_working_directory(os.path.join(self._env.working_directory, '../'))
self.exclude(f'*/{self._config.get_configuration("ProjectName")}.json')
self._output_path = os.path.abspath(os.path.join(self._output_path, self._project_settings.name, 'publish'))
Console.write_line('Build:') Console.write_line('Build:')
Console.spinner('Reading source files:', self._read_sources, text_foreground_color=ForegroundColorEnum.green, spinner_foreground_color=ForegroundColorEnum.blue) Console.spinner(
Console.spinner('Creating internal packages:', self._create_packages, text_foreground_color=ForegroundColorEnum.green, spinner_foreground_color=ForegroundColorEnum.blue) 'Reading source files:',
Console.spinner('Building application:', self._dist_files, text_foreground_color=ForegroundColorEnum.green, spinner_foreground_color=ForegroundColorEnum.blue) 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.write_line('\nPublish:')
Console.spinner('Generating setup.py:', self._create_setup, text_foreground_color=ForegroundColorEnum.green, spinner_foreground_color=ForegroundColorEnum.blue) 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') Console.write_line('Running setup.py:\n')
self._run_setup() self._run_setup()
Console.spinner('Cleaning dist path:', self._clean_dist_files, text_foreground_color=ForegroundColorEnum.green, spinner_foreground_color=ForegroundColorEnum.blue) Console.spinner(
'Cleaning dist path:',
self._clean_dist_files,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.blue
)

View File

@@ -1,25 +1,25 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
""" """
__title__ = 'cpl_cli.templates.new.console.src' __title__ = 'cpl_cli.source_creator'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -0,0 +1,157 @@
import json
import os
from typing import Optional
from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.console.console import Console
from cpl.utils.string import String
from cpl_cli.configuration.workspace_settings import WorkspaceSettings
from cpl_cli.configuration.workspace_settings_name_enum import WorkspaceSettingsNameEnum
from cpl_cli.source_creator.template_builder import TemplateBuilder
from cpl_cli.templates.new.console.appsettings_json import AppsettingsTemplate
from cpl_cli.templates.new.console.license import LicenseTemplate
from cpl_cli.templates.new.console.readme_py import ReadmeTemplate
from cpl_cli.templates.new.console.source.name.application import ApplicationTemplate
from cpl_cli.templates.new.console.source.name.init import MainInitTemplate
from cpl_cli.templates.new.console.source.name.main import MainWithApplicationHostAndStartupTemplate, \
MainWithoutApplicationBaseTemplate, MainWithApplicationBaseTemplate, MainWithDependencyInjection
from cpl_cli.templates.new.console.source.name.startup import StartupTemplate
from cpl_cli.templates.new.console.source.tests.init import TestsInitTemplate
from cpl_cli.templates.template_file_abc import TemplateFileABC
class ConsoleBuilder:
def __init__(self):
pass
@staticmethod
def _create_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):
ws_dict = {
WorkspaceSettings.__name__: {
WorkspaceSettingsNameEnum.default_project.value: project_name,
WorkspaceSettingsNameEnum.projects.value: projects
}
}
Console.spinner(
f'Creating {path}',
cls._create_file,
path,
ws_dict,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
)
@classmethod
def build(cls, project_path: str, use_application_api: bool, use_startup: bool, use_service_providing: bool,
project_name: str, project_settings: dict, workspace: Optional[WorkspaceSettings]):
"""
Builds the console project files
:param project_path:
:param use_application_api:
:param use_startup:
:param use_service_providing:
:param project_name:
:param project_settings:
:param workspace:
:return:
"""
project_name_snake = String.convert_to_snake_case(project_name)
if workspace is None:
templates: list[TemplateFileABC] = [
LicenseTemplate(),
ReadmeTemplate(),
TestsInitTemplate(),
AppsettingsTemplate(),
MainInitTemplate(project_name, 'src')
]
else:
project_path = os.path.join(
os.path.dirname(project_path),
project_name_snake
)
templates: list[TemplateFileABC] = [
LicenseTemplate(),
ReadmeTemplate(),
AppsettingsTemplate(),
MainInitTemplate('', '')
]
if not os.path.isdir(project_path):
os.makedirs(project_path)
src_rel_path = ''
src_name = ''
if workspace is None:
src_rel_path = 'src/'
src_name = project_name_snake
if use_application_api:
templates.append(ApplicationTemplate(src_name, src_rel_path))
if use_startup:
templates.append(StartupTemplate(src_name, src_rel_path))
templates.append(MainWithApplicationHostAndStartupTemplate(src_name, src_rel_path))
else:
templates.append(MainWithApplicationBaseTemplate(src_name, src_rel_path))
else:
if use_service_providing:
templates.append(MainWithDependencyInjection(src_name, src_rel_path))
else:
templates.append(MainWithoutApplicationBaseTemplate(src_name, src_rel_path))
proj_name = project_name
if workspace is not None:
proj_name = project_name_snake
project_file_path = f'{project_name_snake}/{project_name}.json'
if workspace is None:
src_path = f'{proj_name}/src/{project_name_snake}'
workspace_file_path = f'{proj_name}/cpl-workspace.json'
project_file_path = f'{src_path}/{project_name}.json'
cls._create_workspace(workspace_file_path, project_name, {
project_name: project_file_path
})
else:
workspace.projects[project_name] = f'src/{project_file_path}'
cls._create_workspace('cpl-workspace.json', workspace.default_project, workspace.projects)
Console.spinner(
f'Creating {project_file_path}',
cls._create_file,
project_file_path if workspace is None else f'src/{project_file_path}',
project_settings,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
)
for template in templates:
divider = ''
if template.path != '' and not template.path.endswith('/'):
divider = '/'
Console.spinner(
f'Creating {proj_name}/{template.path}{divider}{template.name}',
TemplateBuilder.build,
project_path,
template,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
)

View File

@@ -0,0 +1,158 @@
import json
import os
from typing import Optional
from cpl.console.foreground_color_enum import ForegroundColorEnum
from cpl.console.console import Console
from cpl.utils.string import String
from cpl_cli.configuration.workspace_settings import WorkspaceSettings
from cpl_cli.configuration.workspace_settings_name_enum import WorkspaceSettingsNameEnum
from cpl_cli.source_creator.template_builder import TemplateBuilder
from cpl_cli.templates.new.library.appsettings_json import AppsettingsTemplate
from cpl_cli.templates.new.library.license import LicenseTemplate
from cpl_cli.templates.new.library.readme_py import ReadmeTemplate
from cpl_cli.templates.new.library.source.name.application import ApplicationTemplate
from cpl_cli.templates.new.library.source.name.init import NameInitTemplate
from cpl_cli.templates.new.library.source.name.main import MainWithApplicationHostAndStartupTemplate, \
MainWithoutApplicationBaseTemplate, MainWithApplicationBaseTemplate, MainWithDependencyInjection
from cpl_cli.templates.new.library.source.name.startup import StartupTemplate
from cpl_cli.templates.new.library.source.tests.init import TestsInitTemplate
from cpl_cli.templates.template_file_abc import TemplateFileABC
class LibraryBuilder:
def __init__(self):
pass
@staticmethod
def _create_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):
ws_dict = {
WorkspaceSettings.__name__: {
WorkspaceSettingsNameEnum.default_project.value: project_name,
WorkspaceSettingsNameEnum.projects.value: projects
}
}
Console.spinner(
f'Creating {path}',
cls._create_file,
path,
ws_dict,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
)
@classmethod
def build(cls, project_path: str, use_application_api: bool, use_startup: bool,
use_service_providing: bool, project_name: str, project_settings: dict,
workspace: Optional[WorkspaceSettings]):
"""
Builds the library project files
:param project_path:
:param use_application_api:
:param use_startup:
:param use_service_providing:
:param project_name:
:param project_settings:
:param workspace:
:return:
"""
project_name_snake = String.convert_to_snake_case(project_name)
if workspace is None:
templates: list[TemplateFileABC] = [
LicenseTemplate(),
ReadmeTemplate(),
TestsInitTemplate(),
NameInitTemplate(project_name, 'src/'),
AppsettingsTemplate()
]
else:
project_path = os.path.join(
os.path.dirname(project_path),
project_name_snake
)
templates: list[TemplateFileABC] = [
LicenseTemplate(),
ReadmeTemplate(),
NameInitTemplate('', ''),
AppsettingsTemplate()
]
if not os.path.isdir(project_path):
os.makedirs(project_path)
src_rel_path = ''
src_name = ''
if workspace is None:
src_rel_path = 'src/'
src_name = project_name_snake
if use_application_api:
templates.append(ApplicationTemplate(src_name, src_rel_path))
if use_startup:
templates.append(StartupTemplate(src_name, src_rel_path))
templates.append(MainWithApplicationHostAndStartupTemplate(src_name, src_rel_path))
else:
templates.append(MainWithApplicationBaseTemplate(src_name, src_rel_path))
else:
if use_service_providing:
templates.append(MainWithDependencyInjection(src_name, src_rel_path))
else:
templates.append(MainWithoutApplicationBaseTemplate(src_name, src_rel_path))
proj_name = project_name
if workspace is not None:
proj_name = project_name_snake
project_file_path = f'{project_name_snake}/{project_name}.json'
if workspace is None:
src_path = f'{proj_name}/src/{project_name_snake}'
workspace_file_path = f'{proj_name}/cpl-workspace.json'
project_file_path = f'{src_path}/{project_name}.json'
cls._create_workspace(workspace_file_path, project_name, {
project_name: project_file_path
})
else:
workspace.projects[project_name] = f'src/{project_file_path}'
cls._create_workspace('cpl-workspace.json', workspace.default_project, workspace.projects)
Console.spinner(
f'Creating {project_file_path}',
cls._create_file,
project_file_path if workspace is None else f'src/{project_file_path}',
project_settings,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
)
for template in templates:
divider = ''
if template.path != '' and not template.path.endswith('/'):
divider = '/'
Console.spinner(
f'Creating {proj_name}/{template.path}{divider}{template.name}',
TemplateBuilder.build,
project_path,
template,
text_foreground_color=ForegroundColorEnum.green,
spinner_foreground_color=ForegroundColorEnum.cyan
)

View File

@@ -0,0 +1,24 @@
import os
from cpl_cli.templates.template_file_abc import TemplateFileABC
class TemplateBuilder:
@staticmethod
def build(project_path: str, template: TemplateFileABC):
"""
Creates template
:param project_path:
:param template:
:return:
"""
file_path = os.path.join(project_path, template.path, template.name)
file_rel_path = os.path.join(project_path, template.path)
if not os.path.isdir(file_rel_path):
os.makedirs(file_rel_path)
with open(file_path, 'w') as license_file:
license_file.write(template.value)
license_file.close()

View File

@@ -1,13 +1,17 @@
from cpl.application.application_runtime_abc import ApplicationRuntimeABC import os
from cpl.application.startup_abc import StartupABC from cpl.application.startup_abc import StartupABC
from cpl.configuration.console_argument import ConsoleArgument from cpl.configuration.console_argument import ConsoleArgument
from cpl.configuration.configuration_abc import ConfigurationABC from cpl.configuration.configuration_abc import ConfigurationABC
from cpl.dependency_injection.service_collection_abc import ServiceCollectionABC
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
from cpl_cli.command.add_service import AddService
from cpl_cli.command.build_service import BuildService from cpl_cli.command.build_service import BuildService
from cpl_cli.command.generate_service import GenerateService from cpl_cli.command.generate_service import GenerateService
from cpl_cli.command.install_service import InstallService from cpl_cli.command.install_service import InstallService
from cpl_cli.command.new_service import NewService from cpl_cli.command.new_service import NewService
from cpl_cli.command.publish_service import PublishService from cpl_cli.command.publish_service import PublishService
from cpl_cli.command.remove_service import RemoveService
from cpl_cli.command.start_service import StartService from cpl_cli.command.start_service import StartService
from cpl_cli.command.uninstall_service import UninstallService from cpl_cli.command.uninstall_service import UninstallService
from cpl_cli.command.update_service import UpdateService from cpl_cli.command.update_service import UpdateService
@@ -22,22 +26,24 @@ from cpl_cli.publish.publisher_abc import PublisherABC
class Startup(StartupABC): class Startup(StartupABC):
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC, services: ServiceProviderABC): def __init__(self, config: ConfigurationABC, services: ServiceCollectionABC):
StartupABC.__init__(self) StartupABC.__init__(self)
self._configuration = config self._configuration = config
self._application_runtime = runtime self._env = self._configuration.environment
self._services = services self._services = services
self._application_runtime.set_runtime_directory(__file__) self._env.set_runtime_directory(os.path.dirname(__file__))
def configure_configuration(self) -> ConfigurationABC: def configure_configuration(self) -> ConfigurationABC:
self._configuration.argument_error_function = Error.error self._configuration.argument_error_function = Error.error
self._configuration.add_environment_variables('PYTHON_') self._configuration.add_environment_variables('PYTHON_')
self._configuration.add_environment_variables('CPL_') self._configuration.add_environment_variables('CPL_')
self._configuration.add_json_file('appsettings.json', path=self._application_runtime.runtime_directory, self._configuration.add_json_file('appsettings.json', path=self._env.runtime_directory,
optional=False, output=False) optional=False, output=False)
self._configuration.add_console_argument(ConsoleArgument('', 'add', ['a', 'a'], ' '))
self._configuration.add_console_argument(ConsoleArgument('', 'build', ['b', 'B'], '')) self._configuration.add_console_argument(ConsoleArgument('', 'build', ['b', 'B'], ''))
self._configuration.add_console_argument(ConsoleArgument('', 'generate', ['g', 'G'], '', console_arguments=[ self._configuration.add_console_argument(ConsoleArgument('', 'generate', ['g', 'G'], '', console_arguments=[
ConsoleArgument('', 'abc', ['a', 'A'], ' '), ConsoleArgument('', 'abc', ['a', 'A'], ' '),
@@ -47,18 +53,25 @@ class Startup(StartupABC):
ConsoleArgument('', 'settings', ['st', 'ST'], ' '), ConsoleArgument('', 'settings', ['st', 'ST'], ' '),
ConsoleArgument('', 'thread', ['t', 't'], ' ') ConsoleArgument('', 'thread', ['t', 't'], ' ')
])) ]))
self._configuration.add_console_argument(ConsoleArgument('', 'help', ['h', 'H'], '')) self._configuration.add_console_argument(
ConsoleArgument('', 'help', ['h', 'H'], ' ', is_value_token_optional=True)
)
self._configuration.add_console_argument( self._configuration.add_console_argument(
ConsoleArgument('', 'install', ['i', 'I'], ' ', is_value_token_optional=True) ConsoleArgument('', 'install', ['i', 'I'], ' ', is_value_token_optional=True)
) )
self._configuration.add_console_argument(ConsoleArgument('', 'new', ['n', 'N'], '', console_arguments=[ self._configuration.add_console_argument(ConsoleArgument('', 'new', ['n', 'N'], '', console_arguments=[
ConsoleArgument('', 'console', ['c', 'C'], ' ') ConsoleArgument('', 'console', ['c', 'C'], ' '),
ConsoleArgument('', 'library', ['l', 'L'], ' ')
])) ]))
self._configuration.add_console_argument(ConsoleArgument('', 'publish', ['p', 'P'], '')) self._configuration.add_console_argument(ConsoleArgument('', 'publish', ['p', 'P'], ''))
self._configuration.add_console_argument(ConsoleArgument('', 'remove', ['r', 'R'], ' '))
self._configuration.add_console_argument(ConsoleArgument('', 'start', ['s', 'S'], '')) self._configuration.add_console_argument(ConsoleArgument('', 'start', ['s', 'S'], ''))
self._configuration.add_console_argument(ConsoleArgument('', 'uninstall', ['ui', 'UI'], ' ')) self._configuration.add_console_argument(ConsoleArgument('', 'uninstall', ['ui', 'UI'], ' '))
self._configuration.add_console_argument(ConsoleArgument('', 'update', ['u', 'U'], '')) self._configuration.add_console_argument(ConsoleArgument('', 'update', ['u', 'U'], ''))
self._configuration.add_console_argument(ConsoleArgument('', 'version', ['v', 'V'], '')) self._configuration.add_console_argument(ConsoleArgument('', 'version', ['v', 'V'], ''))
self._configuration.add_console_argument(ConsoleArgument('', '--help', ['-h', '-H'], ''))
self._configuration.add_console_arguments(error=False) self._configuration.add_console_arguments(error=False)
return self._configuration return self._configuration
@@ -69,15 +82,17 @@ class Startup(StartupABC):
self._services.add_transient(PublisherABC, PublisherService) self._services.add_transient(PublisherABC, PublisherService)
self._services.add_transient(LiveServerService) self._services.add_transient(LiveServerService)
self._services.add_transient(AddService)
self._services.add_transient(BuildService) self._services.add_transient(BuildService)
self._services.add_transient(GenerateService) self._services.add_transient(GenerateService)
self._services.add_transient(HelpService) self._services.add_transient(HelpService)
self._services.add_transient(InstallService) self._services.add_transient(InstallService)
self._services.add_transient(NewService) self._services.add_transient(NewService)
self._services.add_transient(PublishService) self._services.add_transient(PublishService)
self._services.add_transient(RemoveService)
self._services.add_transient(StartService) self._services.add_transient(StartService)
self._services.add_transient(UninstallService) self._services.add_transient(UninstallService)
self._services.add_transient(UpdateService) self._services.add_transient(UpdateService)
self._services.add_transient(VersionService) self._services.add_transient(VersionService)
return self._services return self._services.build_service_provider()

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
@@ -15,11 +15,11 @@ __title__ = 'cpl_cli.templates'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
@@ -15,11 +15,11 @@ __title__ = 'cpl_cli.templates.build'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
@@ -15,11 +15,11 @@ __title__ = 'cpl_cli.templates.generate'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -0,0 +1,32 @@
import textwrap
from string import Template
from cpl.utils.string import String
from cpl_cli.templates.template_file_abc import TemplateFileABC
class InitTemplate(TemplateFileABC):
def __init__(self, name: str, schematic: str, schematic_upper: str, path: str):
TemplateFileABC.__init__(self)
self._name = f'__init__.py'
self._class_name = f'{String.first_to_upper(name)}{schematic_upper}'
self._path = path
self._value = textwrap.dedent("""\
# imports
""")
@property
def name(self) -> str:
return self._name
@property
def path(self) -> str:
return self._path
@property
def value(self) -> str:
return Template(self._value).substitute(
Name=self._class_name
)

View File

@@ -14,13 +14,10 @@ class ServiceTemplate(TemplateFileABC):
self._class_name = f'{String.first_to_upper(name)}{schematic_upper}' self._class_name = f'{String.first_to_upper(name)}{schematic_upper}'
self._path = path self._path = path
self._value = textwrap.dedent("""\ self._value = textwrap.dedent("""\
from cpl.dependency_injection import ServiceABC class $Name:
class $Name(ServiceABC):
def __init__(self): def __init__(self):
ServiceABC.__init__(self) pass
""") """)
@property @property

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
@@ -15,11 +15,11 @@ __title__ = 'cpl_cli.templates.new'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
sh_cpl sh-edraft Common Python library sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de :copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details. :license: MIT, see LICENSE for more details.
@@ -15,11 +15,11 @@ __title__ = 'cpl_cli.templates.new.console'
__author__ = 'Sven Heidemann' __author__ = 'Sven Heidemann'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de' __copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4.1.post15' __version__ = '2021.4'
from collections import namedtuple from collections import namedtuple
# imports: # imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro') VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='04', micro='01-15') version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -0,0 +1,41 @@
import textwrap
from cpl_cli.templates.template_file_abc import TemplateFileABC
class AppsettingsTemplate(TemplateFileABC):
def __init__(self):
TemplateFileABC.__init__(self)
self._name = 'appsettings.json'
self._path = ''
self._value = 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"
}
}
""")
@property
def name(self) -> str:
return self._name
@property
def path(self) -> str:
return self._path
@property
def value(self) -> str:
return self._value

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
"""
sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'cpl_cli.templates.new.console.source'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4'
from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
"""
sh_cpl-cli sh-edraft Common Python library CLI
~~~~~~~~~~~~~~~~~~~
sh-edraft Common Python library Command Line Interface
:copyright: (c) 2020 - 2021 sh-edraft.de
:license: MIT, see LICENSE for more details.
"""
__title__ = 'cpl_cli.templates.new.console.source.name'
__author__ = 'Sven Heidemann'
__license__ = 'MIT'
__copyright__ = 'Copyright (c) 2020 - 2021 sh-edraft.de'
__version__ = '2021.4'
from collections import namedtuple
# imports:
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
version_info = VersionInfo(major='2021', minor='4', micro='None')

View File

@@ -1,27 +1,29 @@
import os.path
import textwrap import textwrap
from cpl.utils.string import String
from cpl_cli.templates.template_file_abc import TemplateFileABC from cpl_cli.templates.template_file_abc import TemplateFileABC
class ApplicationTemplate(TemplateFileABC): class ApplicationTemplate(TemplateFileABC):
def __init__(self): def __init__(self, name: str, path: str):
TemplateFileABC.__init__(self) TemplateFileABC.__init__(self)
name = String.convert_to_snake_case(name)
self._name = 'application.py' self._name = 'application.py'
self._path = 'src/' self._path = os.path.join(path, name)
self._value = textwrap.dedent("""\ self._value = textwrap.dedent("""\
from cpl.application.application_abc import ApplicationABC from cpl.application import ApplicationABC
from cpl.application.application_runtime_abc import ApplicationRuntimeABC from cpl.configuration import ConfigurationABC
from cpl.configuration.configuration_abc import ConfigurationABC from cpl.console import Console
from cpl.console.console import Console from cpl.dependency_injection import ServiceProviderABC
from cpl.dependency_injection.service_provider_abc import ServiceProviderABC
class Application(ApplicationABC): class Application(ApplicationABC):
def __init__(self, config: ConfigurationABC, runtime: ApplicationRuntimeABC, services: ServiceProviderABC): def __init__(self, config: ConfigurationABC, services: ServiceProviderABC):
ApplicationABC.__init__(self, config, runtime, services) ApplicationABC.__init__(self, config, services)
def configure(self): def configure(self):
pass pass

View File

@@ -0,0 +1,30 @@
import os.path
import textwrap
from cpl.utils.string import String
from cpl_cli.templates.template_file_abc import TemplateFileABC
class MainInitTemplate(TemplateFileABC):
def __init__(self, name: str, path: str):
TemplateFileABC.__init__(self)
name = String.convert_to_snake_case(name)
self._name = '__init__.py'
self._path = os.path.join(path, name)
self._value = textwrap.dedent("""\
# imports:
""")
@property
def name(self) -> str:
return self._name
@property
def path(self) -> str:
return self._path
@property
def value(self) -> str:
return self._value

View File

@@ -0,0 +1,179 @@
import os.path
import textwrap
from cpl.utils.string import String
from cpl_cli.templates.template_file_abc import TemplateFileABC
class MainWithApplicationHostAndStartupTemplate(TemplateFileABC):
def __init__(self, name: str, path: str):
TemplateFileABC.__init__(self)
name = String.convert_to_snake_case(name)
self._name = 'main.py'
self._path = os.path.join(path, name)
import_pkg = f'{name}.'
if name == '':
import_pkg = ''
self._value = textwrap.dedent(f"""\
from cpl.application import ApplicationBuilder
from {import_pkg}application import Application
from {import_pkg}startup import Startup
def main():
app_builder = ApplicationBuilder(Application)
app_builder.use_startup(Startup)
app_builder.build().run()
if __name__ == '__main__':
main()
""")
@property
def name(self) -> str:
return self._name
@property
def path(self) -> str:
return self._path
@property
def value(self) -> str:
return self._value
class MainWithApplicationBaseTemplate(TemplateFileABC):
def __init__(self, name: str, path: str):
TemplateFileABC.__init__(self)
name = String.convert_to_snake_case(name)
self._name = 'main.py'
self._path = os.path.join(path, name)
import_pkg = f'{name}.'
if name == '':
import_pkg = ''
self._value = textwrap.dedent(f"""\
from cpl.application import ApplicationBuilder
from {import_pkg}application import Application
def main():
app_builder = ApplicationBuilder(Application)
app_builder.build().run()
if __name__ == '__main__':
main()
""")
@property
def name(self) -> str:
return self._name
@property
def path(self) -> str:
return self._path
@property
def value(self) -> str:
return self._value
class MainWithoutApplicationBaseTemplate(TemplateFileABC):
def __init__(self, name: str, path: str):
TemplateFileABC.__init__(self)
name = String.convert_to_snake_case(name)
self._name = 'main.py'
self._path = os.path.join(path, name)
import_pkg = f'{name}.'
if name == '':
import_pkg = ''
self._value = textwrap.dedent("""\
from cpl.console import Console
def main():
Console.write_line('Hello World')
if __name__ == '__main__':
main()
""")
@property
def name(self) -> str:
return self._name
@property
def path(self) -> str:
return self._path
@property
def value(self) -> str:
return self._value
class MainWithDependencyInjection(TemplateFileABC):
def __init__(self, name: str, path: str):
TemplateFileABC.__init__(self)
name = String.convert_to_snake_case(name)
self._name = 'main.py'
self._path = os.path.join(path, name)
import_pkg = f'{name}.'
if name == '':
import_pkg = ''
self._value = textwrap.dedent("""\
from cpl.configuration import Configuration, ConfigurationABC
from cpl.console import Console
from cpl.dependency_injection import ServiceCollection, ServiceProviderABC
def configure_configuration() -> ConfigurationABC:
config = Configuration()
return config
def configure_services(config: ConfigurationABC) -> ServiceProviderABC:
services = ServiceCollection(config)
return services.build_service_provider()
def main():
config = configure_configuration()
provider = configure_services(config)
Console.write_line('Hello World')
if __name__ == '__main__':
main()
""")
@property
def name(self) -> str:
return self._name
@property
def path(self) -> str:
return self._path
@property
def value(self) -> str:
return self._value

View File

@@ -0,0 +1,48 @@
import os.path
import textwrap
from cpl.utils.string import String
from cpl_cli.templates.template_file_abc import TemplateFileABC
class StartupTemplate(TemplateFileABC):
def __init__(self, name: str, path: str):
TemplateFileABC.__init__(self)
name = String.convert_to_snake_case(name)
self._name = 'startup.py'
self._path = os.path.join(path, name)
self._value = textwrap.dedent("""\
from cpl.application import StartupABC
from cpl.configuration import ConfigurationABC
from cpl.dependency_injection import ServiceProviderABC, ServiceCollectionABC
class Startup(StartupABC):
def __init__(self, config: ConfigurationABC, services: ServiceCollectionABC):
StartupABC.__init__(self)
self._configuration = config
self._environment = self._configuration.environment
self._services = services
def configure_configuration(self) -> ConfigurationABC:
return self._configuration
def configure_services(self) -> ServiceProviderABC:
return self._services.build_service_provider()
""")
@property
def name(self) -> str:
return self._name
@property
def path(self) -> str:
return self._path
@property
def value(self) -> str:
return self._value

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