instant-python 0.20.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- instant_python/__init__.py +1 -0
- instant_python/cli/__init__.py +0 -0
- instant_python/cli/cli.py +58 -0
- instant_python/cli/instant_python_typer.py +35 -0
- instant_python/config/__init__.py +0 -0
- instant_python/config/application/__init__.py +0 -0
- instant_python/config/application/config_generator.py +23 -0
- instant_python/config/delivery/__init__.py +0 -0
- instant_python/config/delivery/cli.py +19 -0
- instant_python/config/domain/__init__.py +0 -0
- instant_python/config/domain/question_wizard.py +7 -0
- instant_python/config/infra/__init__.py +0 -0
- instant_python/config/infra/question_wizard/__init__.py +0 -0
- instant_python/config/infra/question_wizard/questionary_console_wizard.py +25 -0
- instant_python/config/infra/question_wizard/step/__init__.py +0 -0
- instant_python/config/infra/question_wizard/step/dependencies_step.py +64 -0
- instant_python/config/infra/question_wizard/step/general_step.py +81 -0
- instant_python/config/infra/question_wizard/step/git_step.py +41 -0
- instant_python/config/infra/question_wizard/step/questionary.py +17 -0
- instant_python/config/infra/question_wizard/step/steps.py +21 -0
- instant_python/config/infra/question_wizard/step/template_step.py +63 -0
- instant_python/initialize/__init__.py +0 -0
- instant_python/initialize/application/__init__.py +0 -0
- instant_python/initialize/application/project_initializer.py +47 -0
- instant_python/initialize/delivery/__init__.py +0 -0
- instant_python/initialize/delivery/cli.py +48 -0
- instant_python/initialize/domain/__init__.py +0 -0
- instant_python/initialize/domain/env_manager.py +9 -0
- instant_python/initialize/domain/node.py +73 -0
- instant_python/initialize/domain/project_formatter.py +7 -0
- instant_python/initialize/domain/project_renderer.py +10 -0
- instant_python/initialize/domain/project_structure.py +77 -0
- instant_python/initialize/domain/project_writer.py +20 -0
- instant_python/initialize/domain/version_control_configurer.py +9 -0
- instant_python/initialize/infra/__init__.py +0 -0
- instant_python/initialize/infra/env_manager/__init__.py +0 -0
- instant_python/initialize/infra/env_manager/env_manager_factory.py +27 -0
- instant_python/initialize/infra/env_manager/pdm_env_manager.py +66 -0
- instant_python/initialize/infra/env_manager/system_console.py +65 -0
- instant_python/initialize/infra/env_manager/uv_env_manager.py +74 -0
- instant_python/initialize/infra/formatter/__init__.py +0 -0
- instant_python/initialize/infra/formatter/ruff_project_formatter.py +10 -0
- instant_python/initialize/infra/renderer/__init__.py +0 -0
- instant_python/initialize/infra/renderer/jinja_environment.py +71 -0
- instant_python/initialize/infra/renderer/jinja_project_renderer.py +57 -0
- instant_python/initialize/infra/version_control/__init__.py +0 -0
- instant_python/initialize/infra/version_control/git_configurer.py +29 -0
- instant_python/initialize/infra/writer/__init__.py +0 -0
- instant_python/initialize/infra/writer/file_system_project_writer.py +23 -0
- instant_python/shared/__init__.py +0 -0
- instant_python/shared/application_error.py +8 -0
- instant_python/shared/domain/__init__.py +0 -0
- instant_python/shared/domain/config_repository.py +18 -0
- instant_python/shared/domain/config_schema.py +113 -0
- instant_python/shared/domain/dependency_config.py +41 -0
- instant_python/shared/domain/general_config.py +71 -0
- instant_python/shared/domain/git_config.py +32 -0
- instant_python/shared/domain/template_config.py +76 -0
- instant_python/shared/infra/__init__.py +0 -0
- instant_python/shared/infra/persistence/__init__.py +0 -0
- instant_python/shared/infra/persistence/yaml_config_repository.py +39 -0
- instant_python/shared/supported_built_in_features.py +20 -0
- instant_python/shared/supported_licenses.py +11 -0
- instant_python/shared/supported_managers.py +10 -0
- instant_python/shared/supported_python_versions.py +12 -0
- instant_python/shared/supported_templates.py +12 -0
- instant_python/templates/boilerplate/.gitignore +164 -0
- instant_python/templates/boilerplate/.pre-commit-config.yml +73 -0
- instant_python/templates/boilerplate/.python-version +1 -0
- instant_python/templates/boilerplate/CITATION.cff +13 -0
- instant_python/templates/boilerplate/LICENSE +896 -0
- instant_python/templates/boilerplate/README.md +8 -0
- instant_python/templates/boilerplate/SECURITY.md +43 -0
- instant_python/templates/boilerplate/event_bus/__init__.py +0 -0
- instant_python/templates/boilerplate/event_bus/domain_event.py +15 -0
- instant_python/templates/boilerplate/event_bus/domain_event_json_deserializer.py +25 -0
- instant_python/templates/boilerplate/event_bus/domain_event_json_serializer.py +16 -0
- instant_python/templates/boilerplate/event_bus/domain_event_subscriber.py +33 -0
- instant_python/templates/boilerplate/event_bus/event_aggregate.py +19 -0
- instant_python/templates/boilerplate/event_bus/event_bus.py +9 -0
- instant_python/templates/boilerplate/event_bus/exchange_type.py +14 -0
- instant_python/templates/boilerplate/event_bus/mock_event_bus.py +16 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_configurer.py +45 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_connection.py +71 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_consumer.py +56 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_event_bus.py +26 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_queue_formatter.py +21 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_settings.py +8 -0
- instant_python/templates/boilerplate/exceptions/__init__.py +0 -0
- instant_python/templates/boilerplate/exceptions/base_error.py +13 -0
- instant_python/templates/boilerplate/exceptions/domain_error.py +6 -0
- instant_python/templates/boilerplate/exceptions/domain_event_type_not_found_error.py +6 -0
- instant_python/templates/boilerplate/exceptions/rabbit_mq_connection_not_established_error.py +7 -0
- instant_python/templates/boilerplate/exceptions/required_value_error.py +6 -0
- instant_python/templates/boilerplate/fastapi/__init__.py +0 -0
- instant_python/templates/boilerplate/fastapi/application.py +74 -0
- instant_python/templates/boilerplate/fastapi/error_handlers.py +88 -0
- instant_python/templates/boilerplate/fastapi/error_response.py +31 -0
- instant_python/templates/boilerplate/fastapi/fastapi_log_middleware.py +32 -0
- instant_python/templates/boilerplate/fastapi/lifespan.py +13 -0
- instant_python/templates/boilerplate/fastapi/success_response.py +13 -0
- instant_python/templates/boilerplate/github/action.yml +35 -0
- instant_python/templates/boilerplate/github/bug_report.yml +60 -0
- instant_python/templates/boilerplate/github/ci.yml +199 -0
- instant_python/templates/boilerplate/github/feature_request.yml +21 -0
- instant_python/templates/boilerplate/github/release.yml +94 -0
- instant_python/templates/boilerplate/logger/__init__.py +0 -0
- instant_python/templates/boilerplate/logger/file_logger.py +55 -0
- instant_python/templates/boilerplate/logger/file_rotating_handler.py +36 -0
- instant_python/templates/boilerplate/logger/json_formatter.py +16 -0
- instant_python/templates/boilerplate/mypy.ini +41 -0
- instant_python/templates/boilerplate/persistence/__init__.py +0 -0
- instant_python/templates/boilerplate/persistence/alembic_migrator.py +19 -0
- instant_python/templates/boilerplate/persistence/async/README.md +1 -0
- instant_python/templates/boilerplate/persistence/async/__init__.py +0 -0
- instant_python/templates/boilerplate/persistence/async/alembic.ini +124 -0
- instant_python/templates/boilerplate/persistence/async/async_engine_fixture.py +20 -0
- instant_python/templates/boilerplate/persistence/async/async_session.py +20 -0
- instant_python/templates/boilerplate/persistence/async/env.py +94 -0
- instant_python/templates/boilerplate/persistence/async/models_metadata.py +10 -0
- instant_python/templates/boilerplate/persistence/async/postgres_settings.py +15 -0
- instant_python/templates/boilerplate/persistence/async/script.py.mako +26 -0
- instant_python/templates/boilerplate/persistence/async/sqlalchemy_repository.py +28 -0
- instant_python/templates/boilerplate/persistence/base.py +4 -0
- instant_python/templates/boilerplate/persistence/synchronous/__init__.py +0 -0
- instant_python/templates/boilerplate/persistence/synchronous/session_maker.py +21 -0
- instant_python/templates/boilerplate/persistence/synchronous/sqlalchemy_repository.py +40 -0
- instant_python/templates/boilerplate/pyproject.toml +134 -0
- instant_python/templates/boilerplate/pytest.ini +10 -0
- instant_python/templates/boilerplate/scripts/add_dependency.py +45 -0
- instant_python/templates/boilerplate/scripts/create_aggregate.py +33 -0
- instant_python/templates/boilerplate/scripts/insert_template.py +90 -0
- instant_python/templates/boilerplate/scripts/integration.sh +39 -0
- instant_python/templates/boilerplate/scripts/local_setup.py +12 -0
- instant_python/templates/boilerplate/scripts/makefile +184 -0
- instant_python/templates/boilerplate/scripts/post-merge.py +40 -0
- instant_python/templates/boilerplate/scripts/pre-commit.py +15 -0
- instant_python/templates/boilerplate/scripts/pre-push.py +6 -0
- instant_python/templates/boilerplate/scripts/remove_dependency.py +40 -0
- instant_python/templates/boilerplate/scripts/unit.sh +40 -0
- instant_python/templates/project_structure/clean_architecture/layers/application.yml +3 -0
- instant_python/templates/project_structure/clean_architecture/layers/delivery.yml +8 -0
- instant_python/templates/project_structure/clean_architecture/layers/domain.yml +10 -0
- instant_python/templates/project_structure/clean_architecture/layers/infra.yml +12 -0
- instant_python/templates/project_structure/clean_architecture/layers/test_application.yml +3 -0
- instant_python/templates/project_structure/clean_architecture/layers/test_delivery.yml +3 -0
- instant_python/templates/project_structure/clean_architecture/layers/test_domain.yml +3 -0
- instant_python/templates/project_structure/clean_architecture/layers/test_infra.yml +9 -0
- instant_python/templates/project_structure/clean_architecture/main_structure.yml +38 -0
- instant_python/templates/project_structure/clean_architecture/source.yml +9 -0
- instant_python/templates/project_structure/clean_architecture/test.yml +9 -0
- instant_python/templates/project_structure/config_files/gitignore.yml +3 -0
- instant_python/templates/project_structure/config_files/mypy.yml +4 -0
- instant_python/templates/project_structure/config_files/pyproject.yml +4 -0
- instant_python/templates/project_structure/config_files/pytest.yml +4 -0
- instant_python/templates/project_structure/config_files/python_version.yml +3 -0
- instant_python/templates/project_structure/documentation/citation.yml +4 -0
- instant_python/templates/project_structure/documentation/license.yml +3 -0
- instant_python/templates/project_structure/documentation/readme.yml +4 -0
- instant_python/templates/project_structure/documentation/security.yml +4 -0
- instant_python/templates/project_structure/domain_driven_design/layers/bounded_context.yml +17 -0
- instant_python/templates/project_structure/domain_driven_design/layers/delivery.yml +8 -0
- instant_python/templates/project_structure/domain_driven_design/layers/shared.yml +18 -0
- instant_python/templates/project_structure/domain_driven_design/layers/shared_domain.yml +10 -0
- instant_python/templates/project_structure/domain_driven_design/layers/shared_infra.yml +12 -0
- instant_python/templates/project_structure/domain_driven_design/layers/test_shared.yml +8 -0
- instant_python/templates/project_structure/domain_driven_design/layers/test_shared_delivery.yml +3 -0
- instant_python/templates/project_structure/domain_driven_design/layers/test_shared_domain.yml +3 -0
- instant_python/templates/project_structure/domain_driven_design/layers/test_shared_infra.yml +9 -0
- instant_python/templates/project_structure/domain_driven_design/main_structure.yml +38 -0
- instant_python/templates/project_structure/domain_driven_design/source.yml +10 -0
- instant_python/templates/project_structure/domain_driven_design/test.yml +9 -0
- instant_python/templates/project_structure/errors.yml +12 -0
- instant_python/templates/project_structure/events/event_bus_domain.yml +48 -0
- instant_python/templates/project_structure/events/event_bus_infra.yml +40 -0
- instant_python/templates/project_structure/events/mock_event_bus.yml +4 -0
- instant_python/templates/project_structure/fastapi/fastapi_app.yml +32 -0
- instant_python/templates/project_structure/fastapi/fastapi_domain.yml +12 -0
- instant_python/templates/project_structure/fastapi/fastapi_infra.yml +12 -0
- instant_python/templates/project_structure/github/github_action.yml +24 -0
- instant_python/templates/project_structure/github/github_issues_template.yml +14 -0
- instant_python/templates/project_structure/github/makefile.yml +3 -0
- instant_python/templates/project_structure/github/precommit_hook.yml +4 -0
- instant_python/templates/project_structure/logger.yml +16 -0
- instant_python/templates/project_structure/macros.j2 +73 -0
- instant_python/templates/project_structure/persistence/alembic_migrator.yml +6 -0
- instant_python/templates/project_structure/persistence/async_alembic.yml +27 -0
- instant_python/templates/project_structure/persistence/async_engine_conftest.yml +4 -0
- instant_python/templates/project_structure/persistence/async_sqlalchemy.yml +14 -0
- instant_python/templates/project_structure/persistence/persistence.yml +8 -0
- instant_python/templates/project_structure/persistence/synchronous_sqlalchemy.yml +20 -0
- instant_python/templates/project_structure/standard_project/layers/source_features.yml +13 -0
- instant_python/templates/project_structure/standard_project/layers/test_event_bus.yml +6 -0
- instant_python/templates/project_structure/standard_project/layers/test_features.yml +6 -0
- instant_python/templates/project_structure/standard_project/main_structure.yml +38 -0
- instant_python/templates/project_structure/standard_project/source.yml +5 -0
- instant_python/templates/project_structure/standard_project/test.yml +5 -0
- instant_python-0.20.0.dist-info/METADATA +318 -0
- instant_python-0.20.0.dist-info/RECORD +202 -0
- instant_python-0.20.0.dist-info/WHEEL +4 -0
- instant_python-0.20.0.dist-info/entry_points.txt +2 -0
- instant_python-0.20.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.20.0"
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.panel import Panel
|
|
6
|
+
|
|
7
|
+
from instant_python import __version__
|
|
8
|
+
from instant_python.config.delivery import cli as config
|
|
9
|
+
from instant_python.initialize.delivery import cli as init
|
|
10
|
+
from instant_python.shared.application_error import ApplicationError
|
|
11
|
+
from instant_python.cli.instant_python_typer import InstantPythonTyper
|
|
12
|
+
|
|
13
|
+
app = InstantPythonTyper()
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def version_callback(value: bool) -> None:
|
|
18
|
+
if value:
|
|
19
|
+
console.print(f"instant-python {__version__}")
|
|
20
|
+
raise typer.Exit()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.callback(invoke_without_command=True)
|
|
24
|
+
def main(
|
|
25
|
+
ctx: typer.Context,
|
|
26
|
+
version: Annotated[
|
|
27
|
+
bool | None,
|
|
28
|
+
typer.Option(
|
|
29
|
+
"--version",
|
|
30
|
+
"-V",
|
|
31
|
+
help="Show the application version",
|
|
32
|
+
callback=version_callback,
|
|
33
|
+
is_eager=True,
|
|
34
|
+
),
|
|
35
|
+
] = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
if ctx.invoked_subcommand is None:
|
|
38
|
+
console.print(ctx.get_help())
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
app.add_typer(init.app)
|
|
42
|
+
app.add_typer(config.app)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@app.error_handler(ApplicationError)
|
|
46
|
+
def handle_application_error(exc: ApplicationError) -> None:
|
|
47
|
+
error_panel = Panel(exc.message, title="Error", border_style="red")
|
|
48
|
+
console.print(error_panel)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@app.error_handler(Exception)
|
|
52
|
+
def handle_unexpected_error(exc: Exception) -> None:
|
|
53
|
+
error_panel = Panel(f"An unexpected error occurred: {exc}", title="Error", border_style="red")
|
|
54
|
+
console.print(error_panel)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
app()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
ExceptionType = type[Exception]
|
|
8
|
+
ErrorHandlingCallback = Callable[[Exception], None]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InstantPythonTyper(typer.Typer):
|
|
12
|
+
error_handlers: dict[ExceptionType, ErrorHandlingCallback] = {}
|
|
13
|
+
|
|
14
|
+
def error_handler(self, exc: ExceptionType) -> Callable[[Callable[[Exception], None]], Callable[[Exception], None]]:
|
|
15
|
+
"""Registers a callback function to be called when 'exc' (the given exception) is raised."""
|
|
16
|
+
|
|
17
|
+
def decorator(func: ErrorHandlingCallback) -> Callable[[Exception], None]:
|
|
18
|
+
self.error_handlers[exc] = func
|
|
19
|
+
return func
|
|
20
|
+
|
|
21
|
+
return decorator
|
|
22
|
+
|
|
23
|
+
def __call__(self, *args, **kwargs) -> Any:
|
|
24
|
+
"""Overrides Typer.__call__ so that when we run the CLI,
|
|
25
|
+
we can catch any exception that's raised and see if there's
|
|
26
|
+
a matching error handler for it.
|
|
27
|
+
"""
|
|
28
|
+
try:
|
|
29
|
+
super().__call__(*args, **kwargs)
|
|
30
|
+
except Exception as error:
|
|
31
|
+
for registered_exc_type, handler in self.error_handlers.items():
|
|
32
|
+
if isinstance(error, registered_exc_type):
|
|
33
|
+
handler(error)
|
|
34
|
+
sys.exit(1)
|
|
35
|
+
raise
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from instant_python.shared.domain.config_repository import ConfigRepository
|
|
2
|
+
from instant_python.shared.domain.config_schema import ConfigSchema
|
|
3
|
+
from instant_python.config.domain.question_wizard import QuestionWizard
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ConfigGenerator:
|
|
7
|
+
def __init__(self, question_wizard: QuestionWizard, repository: ConfigRepository) -> None:
|
|
8
|
+
self._question_wizard = question_wizard
|
|
9
|
+
self._repository = repository
|
|
10
|
+
|
|
11
|
+
def execute(self) -> None:
|
|
12
|
+
answers = self._ask_project_configuration_to_user()
|
|
13
|
+
config = self._create_configuration_based_on_answers(answers)
|
|
14
|
+
self._save_configuration(config)
|
|
15
|
+
|
|
16
|
+
def _create_configuration_based_on_answers(self, answers: dict) -> ConfigSchema:
|
|
17
|
+
return ConfigSchema.from_primitives(answers)
|
|
18
|
+
|
|
19
|
+
def _save_configuration(self, config: ConfigSchema) -> None:
|
|
20
|
+
self._repository.write(config)
|
|
21
|
+
|
|
22
|
+
def _ask_project_configuration_to_user(self) -> dict:
|
|
23
|
+
return self._question_wizard.run()
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
from instant_python.config.application.config_generator import ConfigGenerator
|
|
4
|
+
from instant_python.config.infra.question_wizard.step.questionary import Questionary
|
|
5
|
+
from instant_python.config.infra.question_wizard.questionary_console_wizard import QuestionaryConsoleWizard
|
|
6
|
+
from instant_python.shared.infra.persistence.yaml_config_repository import YamlConfigRepository
|
|
7
|
+
|
|
8
|
+
app = typer.Typer()
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@app.command("config", help="Generate the configuration file for a new project")
|
|
12
|
+
def generate_ipy_configuration_file() -> None:
|
|
13
|
+
config_generator = ConfigGenerator(
|
|
14
|
+
question_wizard=QuestionaryConsoleWizard(
|
|
15
|
+
questionary=Questionary(),
|
|
16
|
+
),
|
|
17
|
+
repository=YamlConfigRepository(),
|
|
18
|
+
)
|
|
19
|
+
config_generator.execute()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from instant_python.config.domain.question_wizard import QuestionWizard
|
|
2
|
+
from instant_python.config.infra.question_wizard.step.dependencies_step import DependenciesStep
|
|
3
|
+
from instant_python.config.infra.question_wizard.step.general_step import GeneralStep
|
|
4
|
+
from instant_python.config.infra.question_wizard.step.git_step import GitStep
|
|
5
|
+
from instant_python.config.infra.question_wizard.step.questionary import Questionary
|
|
6
|
+
from instant_python.config.infra.question_wizard.step.steps import Steps
|
|
7
|
+
from instant_python.config.infra.question_wizard.step.template_step import TemplateStep
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class QuestionaryConsoleWizard(QuestionWizard):
|
|
11
|
+
def __init__(self, questionary: Questionary) -> None:
|
|
12
|
+
self._steps = Steps(
|
|
13
|
+
GeneralStep(questionary=questionary),
|
|
14
|
+
TemplateStep(questionary=questionary),
|
|
15
|
+
GitStep(questionary=questionary),
|
|
16
|
+
DependenciesStep(questionary=questionary),
|
|
17
|
+
)
|
|
18
|
+
self._answers = {}
|
|
19
|
+
|
|
20
|
+
def run(self) -> dict:
|
|
21
|
+
for step in self._steps:
|
|
22
|
+
answer = step.run()
|
|
23
|
+
self._answers.update(answer)
|
|
24
|
+
|
|
25
|
+
return self._answers
|
|
File without changes
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from instant_python.config.infra.question_wizard.step.questionary import Questionary
|
|
4
|
+
from instant_python.config.infra.question_wizard.step.steps import Step
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DependenciesStep(Step):
|
|
8
|
+
_KEY = "dependencies"
|
|
9
|
+
|
|
10
|
+
def __init__(self, questionary: Questionary) -> None:
|
|
11
|
+
super().__init__(questionary)
|
|
12
|
+
self._dependencies = []
|
|
13
|
+
|
|
14
|
+
def run(self) -> dict[str, list[dict[str, Union[str, bool]]]]:
|
|
15
|
+
while True:
|
|
16
|
+
if not self._user_wants_to_install_dependencies():
|
|
17
|
+
break
|
|
18
|
+
|
|
19
|
+
name = self._ask_dependency_name()
|
|
20
|
+
if not name:
|
|
21
|
+
print("Dependency name cannot be empty. Let's try again.")
|
|
22
|
+
continue
|
|
23
|
+
version = self._ask_dependency_version()
|
|
24
|
+
is_for_development = self._ask_if_dependency_is_for_development_purpose(name)
|
|
25
|
+
group_name = self._ask_dev_dependency_group_name() if is_for_development else ""
|
|
26
|
+
|
|
27
|
+
self._dependencies.append(
|
|
28
|
+
{
|
|
29
|
+
"name": name,
|
|
30
|
+
"version": version,
|
|
31
|
+
"is_dev": is_for_development,
|
|
32
|
+
"group": group_name,
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return {self._KEY: self._dependencies}
|
|
37
|
+
|
|
38
|
+
def _ask_dev_dependency_group_name(self) -> str:
|
|
39
|
+
return self._questionary.free_text_question(
|
|
40
|
+
message="Specify the name of the group where to install the dependency (leave empty if not applicable)",
|
|
41
|
+
default="",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def _ask_if_dependency_is_for_development_purpose(self, name: str) -> bool:
|
|
45
|
+
return self._questionary.boolean_question(
|
|
46
|
+
message=f"Do you want to install {name} as a dev dependency?",
|
|
47
|
+
default=False,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def _ask_dependency_version(self) -> str:
|
|
51
|
+
return self._questionary.free_text_question(
|
|
52
|
+
message="Enter the version of the dependency you want to install",
|
|
53
|
+
default="latest",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def _ask_dependency_name(self) -> str:
|
|
57
|
+
return self._questionary.free_text_question(
|
|
58
|
+
message="Enter the name of the dependency you want to install",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def _user_wants_to_install_dependencies(self) -> bool:
|
|
62
|
+
return self._questionary.boolean_question(
|
|
63
|
+
message="Do you want to install dependencies?",
|
|
64
|
+
)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from instant_python.config.infra.question_wizard.step.questionary import Questionary
|
|
2
|
+
from instant_python.config.infra.question_wizard.step.steps import Step
|
|
3
|
+
from instant_python.shared.supported_licenses import SupportedLicenses
|
|
4
|
+
from instant_python.shared.supported_managers import SupportedManagers
|
|
5
|
+
from instant_python.shared.supported_python_versions import SupportedPythonVersions
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GeneralStep(Step):
|
|
9
|
+
_KEY = "general"
|
|
10
|
+
|
|
11
|
+
def __init__(self, questionary: Questionary) -> None:
|
|
12
|
+
super().__init__(questionary)
|
|
13
|
+
self._answers = {}
|
|
14
|
+
|
|
15
|
+
def run(self) -> dict[str, dict[str, str]]:
|
|
16
|
+
self._ask_project_slug()
|
|
17
|
+
self._ask_source_folder_name()
|
|
18
|
+
self._ask_project_description()
|
|
19
|
+
self._ask_project_version()
|
|
20
|
+
self._ask_author_name()
|
|
21
|
+
self._ask_license()
|
|
22
|
+
self._ask_python_version()
|
|
23
|
+
self._ask_dependency_manager()
|
|
24
|
+
|
|
25
|
+
return {self._KEY: self._answers}
|
|
26
|
+
|
|
27
|
+
def _ask_project_slug(self) -> None:
|
|
28
|
+
answer = self._questionary.free_text_question(
|
|
29
|
+
message="Enter the name of the project (CANNOT CONTAIN SPACES)",
|
|
30
|
+
default="python-project",
|
|
31
|
+
)
|
|
32
|
+
self._answers["slug"] = answer
|
|
33
|
+
|
|
34
|
+
def _ask_source_folder_name(self) -> None:
|
|
35
|
+
answer = self._questionary.free_text_question(
|
|
36
|
+
message="Enter the name of the source folder",
|
|
37
|
+
default="src",
|
|
38
|
+
)
|
|
39
|
+
self._answers["source_name"] = answer
|
|
40
|
+
|
|
41
|
+
def _ask_project_description(self) -> None:
|
|
42
|
+
answer = self._questionary.free_text_question(
|
|
43
|
+
message="Enter the project description",
|
|
44
|
+
default="Python Project Description",
|
|
45
|
+
)
|
|
46
|
+
self._answers["description"] = answer
|
|
47
|
+
|
|
48
|
+
def _ask_project_version(self) -> None:
|
|
49
|
+
answer = self._questionary.free_text_question(
|
|
50
|
+
message="Enter the project initial version",
|
|
51
|
+
default="0.1.0",
|
|
52
|
+
)
|
|
53
|
+
self._answers["version"] = answer
|
|
54
|
+
|
|
55
|
+
def _ask_author_name(self) -> None:
|
|
56
|
+
answer = self._questionary.free_text_question(
|
|
57
|
+
message="Enter your name",
|
|
58
|
+
)
|
|
59
|
+
self._answers["author"] = answer
|
|
60
|
+
|
|
61
|
+
def _ask_license(self) -> None:
|
|
62
|
+
answer = self._questionary.single_choice_question(
|
|
63
|
+
message="Select a license",
|
|
64
|
+
options=SupportedLicenses.get_supported_licenses(),
|
|
65
|
+
)
|
|
66
|
+
self._answers["license"] = answer
|
|
67
|
+
|
|
68
|
+
def _ask_python_version(self) -> None:
|
|
69
|
+
answer = self._questionary.single_choice_question(
|
|
70
|
+
message="Enter the python version",
|
|
71
|
+
options=SupportedPythonVersions.get_supported_versions(),
|
|
72
|
+
)
|
|
73
|
+
self._answers["python_version"] = answer
|
|
74
|
+
|
|
75
|
+
def _ask_dependency_manager(self) -> None:
|
|
76
|
+
answer = self._questionary.single_choice_question(
|
|
77
|
+
message="Select a dependency manager",
|
|
78
|
+
options=SupportedManagers.get_supported_managers(),
|
|
79
|
+
default=SupportedManagers.UV,
|
|
80
|
+
)
|
|
81
|
+
self._answers["dependency_manager"] = answer
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from instant_python.config.infra.question_wizard.step.questionary import Questionary
|
|
4
|
+
from instant_python.config.infra.question_wizard.step.steps import Step
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GitStep(Step):
|
|
8
|
+
_KEY = "git"
|
|
9
|
+
|
|
10
|
+
def __init__(self, questionary: Questionary) -> None:
|
|
11
|
+
super().__init__(questionary)
|
|
12
|
+
self._answers = {}
|
|
13
|
+
|
|
14
|
+
def run(self) -> dict[str, dict[str, Union[str, bool]]]:
|
|
15
|
+
if not self._user_wants_initialize_git_repository():
|
|
16
|
+
return {self._KEY: self._answers}
|
|
17
|
+
|
|
18
|
+
self._ask_git_username()
|
|
19
|
+
self._ask_git_email()
|
|
20
|
+
|
|
21
|
+
return {self._KEY: self._answers}
|
|
22
|
+
|
|
23
|
+
def _ask_git_email(self) -> None:
|
|
24
|
+
answer = self._questionary.free_text_question(
|
|
25
|
+
message="Type your git email",
|
|
26
|
+
)
|
|
27
|
+
self._answers["email"] = answer
|
|
28
|
+
|
|
29
|
+
def _ask_git_username(self) -> None:
|
|
30
|
+
answer = self._questionary.free_text_question(
|
|
31
|
+
message="Type your git user name",
|
|
32
|
+
)
|
|
33
|
+
self._answers["username"] = answer
|
|
34
|
+
|
|
35
|
+
def _user_wants_initialize_git_repository(self) -> bool:
|
|
36
|
+
answer = self._questionary.boolean_question(
|
|
37
|
+
message="Do you want to initialize a git repository?",
|
|
38
|
+
default=True,
|
|
39
|
+
)
|
|
40
|
+
self._answers["initialize"] = answer
|
|
41
|
+
return answer
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import questionary
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Questionary:
|
|
7
|
+
def boolean_question(self, message: str, default: bool = False) -> bool:
|
|
8
|
+
return questionary.confirm(message, default=default).ask()
|
|
9
|
+
|
|
10
|
+
def free_text_question(self, message: str, default: Optional[str] = None) -> str:
|
|
11
|
+
return questionary.text(message, default=default if default else "").ask()
|
|
12
|
+
|
|
13
|
+
def single_choice_question(self, message: str, options: list[str], default: Optional[str] = None) -> str:
|
|
14
|
+
return questionary.select(message, choices=options, default=default).ask()
|
|
15
|
+
|
|
16
|
+
def multiselect_question(self, message: str, options: list[str]) -> list[str]:
|
|
17
|
+
return questionary.checkbox(message, choices=options).ask()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import Iterator
|
|
3
|
+
|
|
4
|
+
from instant_python.config.infra.question_wizard.step.questionary import Questionary
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Step(ABC):
|
|
8
|
+
def __init__(self, questionary: Questionary) -> None:
|
|
9
|
+
self._questionary = questionary
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def run(self) -> dict[str, dict]:
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Steps:
|
|
17
|
+
def __init__(self, *step: Step) -> None:
|
|
18
|
+
self._steps = list(step)
|
|
19
|
+
|
|
20
|
+
def __iter__(self) -> Iterator[Step]:
|
|
21
|
+
return iter(self._steps)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
from instant_python.config.infra.question_wizard.step.questionary import Questionary
|
|
4
|
+
from instant_python.config.infra.question_wizard.step.steps import Step
|
|
5
|
+
from instant_python.shared.supported_built_in_features import SupportedBuiltInFeatures
|
|
6
|
+
from instant_python.shared.supported_templates import SupportedTemplates
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TemplateStep(Step):
|
|
10
|
+
_KEY = "template"
|
|
11
|
+
|
|
12
|
+
def __init__(self, questionary: Questionary) -> None:
|
|
13
|
+
super().__init__(questionary)
|
|
14
|
+
self._answers = {}
|
|
15
|
+
|
|
16
|
+
def run(self) -> dict[str, dict[str, Union[str, list[str]]]]:
|
|
17
|
+
name = self._choose_template_name_from_options()
|
|
18
|
+
|
|
19
|
+
if name == SupportedTemplates.DDD and self._user_wants_to_specify_bounded_context():
|
|
20
|
+
self._ask_bounded_context_name()
|
|
21
|
+
self._ask_aggregate_name()
|
|
22
|
+
|
|
23
|
+
if name != SupportedTemplates.CUSTOM:
|
|
24
|
+
self._select_built_in_features()
|
|
25
|
+
|
|
26
|
+
return {self._KEY: self._answers}
|
|
27
|
+
|
|
28
|
+
def _choose_template_name_from_options(self) -> str:
|
|
29
|
+
answer = self._questionary.single_choice_question(
|
|
30
|
+
message="Select a template",
|
|
31
|
+
options=SupportedTemplates.get_supported_templates(),
|
|
32
|
+
)
|
|
33
|
+
self._answers["name"] = answer
|
|
34
|
+
return answer
|
|
35
|
+
|
|
36
|
+
def _user_wants_to_specify_bounded_context(self) -> None:
|
|
37
|
+
answer = self._questionary.boolean_question(
|
|
38
|
+
message="Do you want to specify your first bounded context?",
|
|
39
|
+
default=True,
|
|
40
|
+
)
|
|
41
|
+
self._answers["specify_bounded_context"] = answer
|
|
42
|
+
|
|
43
|
+
def _ask_bounded_context_name(self) -> str:
|
|
44
|
+
answer = self._questionary.free_text_question(
|
|
45
|
+
message="Enter the bounded context name",
|
|
46
|
+
default="backoffice",
|
|
47
|
+
)
|
|
48
|
+
self._answers["bounded_context"] = answer
|
|
49
|
+
return answer
|
|
50
|
+
|
|
51
|
+
def _ask_aggregate_name(self) -> None:
|
|
52
|
+
answer = self._questionary.free_text_question(
|
|
53
|
+
message="Enter the aggregate name",
|
|
54
|
+
default="user",
|
|
55
|
+
)
|
|
56
|
+
self._answers["aggregate_name"] = answer
|
|
57
|
+
|
|
58
|
+
def _select_built_in_features(self) -> None:
|
|
59
|
+
answer = self._questionary.multiselect_question(
|
|
60
|
+
message="Select the built-in features you want to include",
|
|
61
|
+
options=SupportedBuiltInFeatures.get_supported_built_in_features(),
|
|
62
|
+
)
|
|
63
|
+
self._answers["built_in_features"] = answer
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from instant_python.shared.domain.config_schema import ConfigSchema
|
|
4
|
+
from instant_python.initialize.domain.env_manager import EnvManager
|
|
5
|
+
from instant_python.initialize.domain.project_formatter import ProjectFormatter
|
|
6
|
+
from instant_python.initialize.domain.project_renderer import ProjectRenderer
|
|
7
|
+
from instant_python.initialize.domain.project_writer import ProjectWriter
|
|
8
|
+
from instant_python.initialize.domain.version_control_configurer import VersionControlConfigurer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ProjectInitializer:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
renderer: ProjectRenderer,
|
|
15
|
+
writer: ProjectWriter,
|
|
16
|
+
env_manager: EnvManager,
|
|
17
|
+
version_control_configurer: VersionControlConfigurer,
|
|
18
|
+
formatter: ProjectFormatter,
|
|
19
|
+
) -> None:
|
|
20
|
+
self._project_renderer = renderer
|
|
21
|
+
self._writer = writer
|
|
22
|
+
self._env_manager = env_manager
|
|
23
|
+
self._version_control_configurer = version_control_configurer
|
|
24
|
+
self._formatter = formatter
|
|
25
|
+
|
|
26
|
+
def execute(self, config: ConfigSchema, destination_project_folder: Path) -> None:
|
|
27
|
+
self._create_project_at_destination_folder(config, destination_project_folder)
|
|
28
|
+
self._setup_development_environment(config)
|
|
29
|
+
if config.version_control_has_to_be_initialized:
|
|
30
|
+
self._setup_version_control_system(config)
|
|
31
|
+
self._format_final_project()
|
|
32
|
+
|
|
33
|
+
def _format_final_project(self) -> None:
|
|
34
|
+
self._formatter.format()
|
|
35
|
+
|
|
36
|
+
def _setup_version_control_system(self, config: ConfigSchema) -> None:
|
|
37
|
+
self._version_control_configurer.setup(config.git)
|
|
38
|
+
|
|
39
|
+
def _setup_development_environment(self, config: ConfigSchema) -> None:
|
|
40
|
+
self._env_manager.setup(
|
|
41
|
+
python_version=config.python_version,
|
|
42
|
+
dependencies=config.dependencies,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def _create_project_at_destination_folder(self, config: ConfigSchema, destination_project_folder: Path) -> None:
|
|
46
|
+
project_structure = self._project_renderer.render(context_config=config)
|
|
47
|
+
self._writer.write(project_structure=project_structure, destination=destination_project_folder)
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from instant_python.initialize.application.project_initializer import ProjectInitializer
|
|
6
|
+
from instant_python.initialize.infra.env_manager.env_manager_factory import EnvManagerFactory
|
|
7
|
+
from instant_python.initialize.infra.env_manager.system_console import SystemConsole
|
|
8
|
+
from instant_python.initialize.infra.formatter.ruff_project_formatter import RuffProjectFormatter
|
|
9
|
+
from instant_python.shared.infra.persistence.yaml_config_repository import YamlConfigRepository
|
|
10
|
+
from instant_python.initialize.infra.renderer.jinja_environment import JinjaEnvironment
|
|
11
|
+
from instant_python.initialize.infra.renderer.jinja_project_renderer import JinjaProjectRenderer
|
|
12
|
+
from instant_python.initialize.infra.version_control.git_configurer import GitConfigurer
|
|
13
|
+
from instant_python.initialize.infra.writer.file_system_project_writer import FileSystemProjectWriter
|
|
14
|
+
|
|
15
|
+
app = typer.Typer()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command("init", help="Create a new project")
|
|
19
|
+
def create_new_project(
|
|
20
|
+
config_file: str = typer.Option(
|
|
21
|
+
"ipy.yml", "--config", "-c", help="Path to yml configuration file. Default: ipy.yml"
|
|
22
|
+
),
|
|
23
|
+
custom_templates_path: str | None = typer.Option(
|
|
24
|
+
None, "--templates", "-t", help="Path to custom templates folder."
|
|
25
|
+
),
|
|
26
|
+
) -> None:
|
|
27
|
+
repository = YamlConfigRepository()
|
|
28
|
+
config = repository.read(path=Path(config_file))
|
|
29
|
+
|
|
30
|
+
current_working_directory = Path.cwd()
|
|
31
|
+
console = SystemConsole(working_directory=str(current_working_directory / config.project_folder_name))
|
|
32
|
+
project_initializer = ProjectInitializer(
|
|
33
|
+
renderer=JinjaProjectRenderer(env=JinjaEnvironment(user_template_path=custom_templates_path)),
|
|
34
|
+
writer=FileSystemProjectWriter(),
|
|
35
|
+
env_manager=EnvManagerFactory.create(dependency_manager=config.dependency_manager, console=console),
|
|
36
|
+
version_control_configurer=GitConfigurer(console=console),
|
|
37
|
+
formatter=RuffProjectFormatter(console=console),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
project_initializer.execute(
|
|
41
|
+
config=config,
|
|
42
|
+
destination_project_folder=current_working_directory / config.project_folder_name,
|
|
43
|
+
)
|
|
44
|
+
repository.move(config=config, base_directory=current_working_directory)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
app()
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from instant_python.shared.domain.dependency_config import DependencyConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class EnvManager(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
def setup(self, python_version: str, dependencies: list[DependencyConfig]) -> None:
|
|
9
|
+
raise NotImplementedError
|