instant-python 0.0.1__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 +0 -0
- instant_python/cli.py +11 -0
- instant_python/folder_cli.py +50 -0
- instant_python/installer/__init__.py +0 -0
- instant_python/installer/dependency_manager.py +15 -0
- instant_python/installer/dependency_manager_factory.py +14 -0
- instant_python/installer/git_configurer.py +47 -0
- instant_python/installer/installer.py +18 -0
- instant_python/installer/managers.py +7 -0
- instant_python/installer/operating_systems.py +7 -0
- instant_python/installer/pdm_manager.py +72 -0
- instant_python/installer/uv_manager.py +73 -0
- instant_python/project_cli.py +100 -0
- instant_python/project_generator/__init__.py +0 -0
- instant_python/project_generator/custom_template_manager.py +20 -0
- instant_python/project_generator/default_template_manager.py +45 -0
- instant_python/project_generator/directory.py +28 -0
- instant_python/project_generator/file.py +20 -0
- instant_python/project_generator/folder_tree.py +40 -0
- instant_python/project_generator/jinja_custom_filters.py +18 -0
- instant_python/project_generator/node.py +14 -0
- instant_python/project_generator/project_generator.py +31 -0
- instant_python/project_generator/template_manager.py +7 -0
- instant_python/question_prompter/__init__.py +0 -0
- instant_python/question_prompter/question/__init__.py +0 -0
- instant_python/question_prompter/question/boolean_question.py +13 -0
- instant_python/question_prompter/question/choice_question.py +18 -0
- instant_python/question_prompter/question/conditional_question.py +25 -0
- instant_python/question_prompter/question/dependencies_question.py +43 -0
- instant_python/question_prompter/question/free_text_question.py +13 -0
- instant_python/question_prompter/question/multiple_choice_question.py +13 -0
- instant_python/question_prompter/question/question.py +15 -0
- instant_python/question_prompter/question_wizard.py +15 -0
- instant_python/question_prompter/step/__init__.py +0 -0
- instant_python/question_prompter/step/dependencies_step.py +20 -0
- instant_python/question_prompter/step/general_custom_template_project_step.py +45 -0
- instant_python/question_prompter/step/general_project_step.py +50 -0
- instant_python/question_prompter/step/git_step.py +23 -0
- instant_python/question_prompter/step/steps.py +16 -0
- instant_python/question_prompter/step/template_step.py +63 -0
- instant_python/question_prompter/template_types.py +7 -0
- instant_python/question_prompter/user_requirements.py +39 -0
- instant_python/templates/__init__.py +0 -0
- instant_python/templates/boilerplate/.gitignore +164 -0
- instant_python/templates/boilerplate/.pre-commit-config.yml +33 -0
- instant_python/templates/boilerplate/.python-version +1 -0
- instant_python/templates/boilerplate/LICENSE +896 -0
- instant_python/templates/boilerplate/event_bus/__init__.py +0 -0
- instant_python/templates/boilerplate/event_bus/aggregate_root.py +19 -0
- instant_python/templates/boilerplate/event_bus/domain_event.py +15 -0
- instant_python/templates/boilerplate/event_bus/domain_event_json_deserializer.py +28 -0
- instant_python/templates/boilerplate/event_bus/domain_event_json_serializer.py +17 -0
- instant_python/templates/boilerplate/event_bus/domain_event_subscriber.py +15 -0
- instant_python/templates/boilerplate/event_bus/event_bus.py +10 -0
- instant_python/templates/boilerplate/event_bus/exchange_type.py +7 -0
- instant_python/templates/boilerplate/event_bus/mock_event_bus.py +18 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_configurer.py +54 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_connection.py +77 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_consumer.py +58 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_event_bus.py +28 -0
- instant_python/templates/boilerplate/event_bus/rabbit_mq_queue_formatter.py +22 -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/domain_error.py +17 -0
- instant_python/templates/boilerplate/exceptions/domain_event_type_not_found_error.py +17 -0
- instant_python/templates/boilerplate/exceptions/incorrect_value_type_error.py +21 -0
- instant_python/templates/boilerplate/exceptions/invalid_id_format_error.py +17 -0
- instant_python/templates/boilerplate/exceptions/invalid_negative_value_error.py +17 -0
- instant_python/templates/boilerplate/exceptions/required_value_error.py +17 -0
- instant_python/templates/boilerplate/fastapi/__init__.py +0 -0
- instant_python/templates/boilerplate/fastapi/application.py +25 -0
- instant_python/templates/boilerplate/fastapi/http_response.py +45 -0
- instant_python/templates/boilerplate/fastapi/lifespan.py +14 -0
- instant_python/templates/boilerplate/fastapi/status_code.py +9 -0
- instant_python/templates/boilerplate/github/action.yml +22 -0
- instant_python/templates/boilerplate/github/test_lint.yml +36 -0
- instant_python/templates/boilerplate/logger/__init__.py +0 -0
- instant_python/templates/boilerplate/logger/json_formatter.py +16 -0
- instant_python/templates/boilerplate/logger/logger.py +39 -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 +20 -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 +21 -0
- instant_python/templates/boilerplate/persistence/async/env.py +95 -0
- instant_python/templates/boilerplate/persistence/async/models_metadata.py +11 -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 +30 -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 +22 -0
- instant_python/templates/boilerplate/persistence/synchronous/sqlalchemy_repository.py +35 -0
- instant_python/templates/boilerplate/pyproject.toml +29 -0
- instant_python/templates/boilerplate/pytest.ini +10 -0
- instant_python/templates/boilerplate/random_generator.py +9 -0
- instant_python/templates/boilerplate/scripts/add_dependency.sh +37 -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.sh +15 -0
- instant_python/templates/boilerplate/scripts/makefile +137 -0
- instant_python/templates/boilerplate/scripts/post-merge +11 -0
- instant_python/templates/boilerplate/scripts/pre-commit +4 -0
- instant_python/templates/boilerplate/scripts/pre-push +6 -0
- instant_python/templates/boilerplate/scripts/remove_dependency.sh +36 -0
- instant_python/templates/boilerplate/scripts/unit.sh +40 -0
- instant_python/templates/boilerplate/value_object/__init__.py +0 -0
- instant_python/templates/boilerplate/value_object/int_value_object.py +11 -0
- instant_python/templates/boilerplate/value_object/string_value_object.py +19 -0
- instant_python/templates/boilerplate/value_object/uuid.py +17 -0
- instant_python/templates/boilerplate/value_object/value_object.py +21 -0
- instant_python/templates/project_structure/alembic_migrator.yml.j2 +3 -0
- instant_python/templates/project_structure/async_alembic.yml.j2 +20 -0
- instant_python/templates/project_structure/async_sqlalchemy.yml.j2 +17 -0
- instant_python/templates/project_structure/clean_architecture/main_structure.yml.j2 +25 -0
- instant_python/templates/project_structure/clean_architecture/source.yml.j2 +51 -0
- instant_python/templates/project_structure/clean_architecture/test.yml.j2 +23 -0
- instant_python/templates/project_structure/domain_driven_design/bounded_context.yml.j2 +20 -0
- instant_python/templates/project_structure/domain_driven_design/main_structure.yml.j2 +25 -0
- instant_python/templates/project_structure/domain_driven_design/source.yml.j2 +55 -0
- instant_python/templates/project_structure/domain_driven_design/test.yml.j2 +26 -0
- instant_python/templates/project_structure/event_bus_domain.yml.j2 +26 -0
- instant_python/templates/project_structure/event_bus_infra.yml.j2 +32 -0
- instant_python/templates/project_structure/fastapi_app.yml.j2 +10 -0
- instant_python/templates/project_structure/fastapi_infra.yml.j2 +10 -0
- instant_python/templates/project_structure/github_action.yml.j2 +18 -0
- instant_python/templates/project_structure/gitignore.yml.j2 +2 -0
- instant_python/templates/project_structure/license.yml.j2 +2 -0
- instant_python/templates/project_structure/logger.yml.j2 +10 -0
- instant_python/templates/project_structure/macros.j2 +6 -0
- instant_python/templates/project_structure/makefile.yml.j2 +38 -0
- instant_python/templates/project_structure/mypy.yml.j2 +3 -0
- instant_python/templates/project_structure/pre_commit.yml.j2 +3 -0
- instant_python/templates/project_structure/pyproject.yml.j2 +3 -0
- instant_python/templates/project_structure/pytest.yml.j2 +3 -0
- instant_python/templates/project_structure/python_version.yml.j2 +2 -0
- instant_python/templates/project_structure/standard_project/main_structure.yml.j2 +25 -0
- instant_python/templates/project_structure/standard_project/source.yml.j2 +30 -0
- instant_python/templates/project_structure/standard_project/test.yml.j2 +16 -0
- instant_python/templates/project_structure/synchronous_sqlalchemy.yml.j2 +17 -0
- instant_python/templates/project_structure/value_objects.yml.j2 +35 -0
- instant_python-0.0.1.dist-info/METADATA +276 -0
- instant_python-0.0.1.dist-info/RECORD +149 -0
- instant_python-0.0.1.dist-info/WHEEL +4 -0
- instant_python-0.0.1.dist-info/entry_points.txt +2 -0
- instant_python-0.0.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
def create_package_structure(base_dir: str, context: str, aggregate: str) -> None:
|
|
4
|
+
"""Create the folder structure and __init__.py files for the given context and aggregate."""
|
|
5
|
+
context_base_path = Path(base_dir, "contexts", context)
|
|
6
|
+
aggregate_base_path = context_base_path / aggregate
|
|
7
|
+
|
|
8
|
+
dirs_to_create = [
|
|
9
|
+
context_base_path,
|
|
10
|
+
aggregate_base_path,
|
|
11
|
+
aggregate_base_path / "application",
|
|
12
|
+
aggregate_base_path / "domain",
|
|
13
|
+
aggregate_base_path / "infra",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
for directory in dirs_to_create:
|
|
17
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
18
|
+
init_file = directory / "__init__.py"
|
|
19
|
+
if not init_file.exists():
|
|
20
|
+
init_file.touch()
|
|
21
|
+
|
|
22
|
+
def main() -> None:
|
|
23
|
+
context = input("Enter the name of the bounded context: ").strip()
|
|
24
|
+
aggregate = input("Enter the name of the new aggregate: ").strip()
|
|
25
|
+
|
|
26
|
+
create_package_structure("instant_python", context, aggregate)
|
|
27
|
+
create_package_structure("tests", context, aggregate)
|
|
28
|
+
|
|
29
|
+
print(f"Successfully created the aggregate '{aggregate}' under context '{context}'.")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
main()
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from scripts.templates.aggregate_root_template import aggregate_root_template
|
|
6
|
+
from scripts.templates.exceptions.incorrect_value_error_template import incorrect_value_type_error_template
|
|
7
|
+
from scripts.templates.exceptions.required_value_error_template import required_value_error_template
|
|
8
|
+
from scripts.templates.exceptions.invalid_negative_value_error_template import invalid_negative_value_error_template
|
|
9
|
+
from scripts.templates.value_objects.string_value_object_template import string_value_object_template
|
|
10
|
+
from scripts.templates.value_objects.uuid_template import uuid_template
|
|
11
|
+
from scripts.templates.value_objects.value_object_template import value_object_template
|
|
12
|
+
from scripts.templates.value_objects.int_value_object_template import int_value_object_template
|
|
13
|
+
from scripts.templates.events.domain_event_template import domain_event_template
|
|
14
|
+
from scripts.templates.events.domain_event_subscriber_template import domain_event_subscriber_template
|
|
15
|
+
from scripts.templates.events.event_bus_template import event_bus_template
|
|
16
|
+
from scripts.templates.events.exchange_type_template import exchange_type_template
|
|
17
|
+
from scripts.templates.events.rabbit_mq_configurer_template import rabbit_mq_configurer_template
|
|
18
|
+
from scripts.templates.events.rabbit_mq_connection_template import rabbit_mq_connection_template
|
|
19
|
+
from scripts.templates.events.rabbit_mq_consumer_template import rabbit_mq_consumer_template
|
|
20
|
+
from scripts.templates.events.rabbit_mq_event_bus_template import rabbit_mq_event_bus_template
|
|
21
|
+
from scripts.templates.events.rabbit_mq_queue_formatter_template import rabbit_mq_queue_formatter_template
|
|
22
|
+
from scripts.templates.events.rabbit_mq_settings_template import rabbit_mq_settings_template
|
|
23
|
+
from scripts.templates.events.domain_event_json_deserializer_template import domain_event_json_deserializer_template
|
|
24
|
+
from scripts.templates.events.domain_event_json_serializer_template import domain_event_json_serializer_template
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
TEMPLATES = {
|
|
28
|
+
"value_object": value_object_template,
|
|
29
|
+
"string_value_object": string_value_object_template,
|
|
30
|
+
"int_value_object": int_value_object_template,
|
|
31
|
+
"uuid": uuid_template,
|
|
32
|
+
"incorrect_value": incorrect_value_type_error_template,
|
|
33
|
+
"required_value": required_value_error_template,
|
|
34
|
+
"invalid_negative_value": invalid_negative_value_error_template,
|
|
35
|
+
"domain_event": domain_event_template,
|
|
36
|
+
"domain_event_subscriber": domain_event_subscriber_template,
|
|
37
|
+
"event_bus": event_bus_template,
|
|
38
|
+
"exchange_type": exchange_type_template,
|
|
39
|
+
"aggregate_root": aggregate_root_template,
|
|
40
|
+
"rabbit_mq_configurer": rabbit_mq_configurer_template,
|
|
41
|
+
"rabbit_mq_connection": rabbit_mq_connection_template,
|
|
42
|
+
"rabbit_mq_consumer": rabbit_mq_consumer_template,
|
|
43
|
+
"rabbit_mq_event_bus": rabbit_mq_event_bus_template,
|
|
44
|
+
"rabbit_mq_queue_formatter": rabbit_mq_queue_formatter_template,
|
|
45
|
+
"rabbit_mq_settings": rabbit_mq_settings_template,
|
|
46
|
+
"domain_event_json_deserializer": domain_event_json_deserializer_template,
|
|
47
|
+
"domain_event_json_serializer": domain_event_json_serializer_template,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main() -> None:
|
|
52
|
+
list_available_templates()
|
|
53
|
+
template_name = input("Enter the name of the template you want to insert: ")
|
|
54
|
+
ensure_template_exists(template_name)
|
|
55
|
+
|
|
56
|
+
user_path = input("Enter the path where template should be created: ")
|
|
57
|
+
folder_path = generate_folder_path(Path(user_path))
|
|
58
|
+
write_content_at(folder_path, template_name)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def list_available_templates() -> None:
|
|
62
|
+
print(f"Available templates: {', '.join(TEMPLATES.keys())}")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def ensure_template_exists(template_name: str) -> None:
|
|
66
|
+
if template_name not in TEMPLATES:
|
|
67
|
+
print(f"Error: Template '{template_name}' not found.")
|
|
68
|
+
list_available_templates()
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def write_content_at(folder_path: Path, template_name: str) -> None:
|
|
73
|
+
file_name = f"{template_name.replace('_template', '')}.py"
|
|
74
|
+
file_path = folder_path / file_name
|
|
75
|
+
|
|
76
|
+
with open(file_path, "w") as file:
|
|
77
|
+
file.write(TEMPLATES[template_name])
|
|
78
|
+
print(f"Template {template_name} created at {file_path}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def generate_folder_path(user_path: Path) -> Path:
|
|
82
|
+
project_root = Path(__file__).resolve().parents[1]
|
|
83
|
+
folder_path = project_root / user_path
|
|
84
|
+
folder_path.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
print(f"Folder created at: {folder_path}")
|
|
86
|
+
return folder_path
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
main()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
function get_bounded_contexts_with_changes {
|
|
4
|
+
changed_files=$(git diff --name-only HEAD)
|
|
5
|
+
bounded_contexts=$(echo "$changed_files" | grep -E 'instant_python/contexts/([^/]*/)*(infra)' | sed -E 's|instant_python/contexts/([^/]*)/.*|\1|' | sort -u)
|
|
6
|
+
echo "$bounded_contexts"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function has_bounded_contexts {
|
|
10
|
+
local contexts="$1"
|
|
11
|
+
|
|
12
|
+
if [[ -z "$contexts" ]]; then
|
|
13
|
+
echo "No changes detected in infra folder of any bounded context."
|
|
14
|
+
return 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
return 0
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function run_tests {
|
|
21
|
+
local contexts="$1"
|
|
22
|
+
|
|
23
|
+
for context in $contexts; do
|
|
24
|
+
echo "Running integration tests for bounded context: $context"
|
|
25
|
+
infra_folders=$(find tests/contexts/"$context" -type d -name "infra")
|
|
26
|
+
{{ dependency_manager }} run pytest $infra_folders -ra
|
|
27
|
+
done
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function main {
|
|
31
|
+
local bounded_contexts
|
|
32
|
+
bounded_contexts=$(get_bounded_contexts_with_changes)
|
|
33
|
+
|
|
34
|
+
if has_bounded_contexts "$bounded_contexts"; then
|
|
35
|
+
run_tests "$bounded_contexts"
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
main
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
function main {
|
|
5
|
+
echo "Installing git hooks..."
|
|
6
|
+
redirect_hooks_location
|
|
7
|
+
echo "Git hooks installed."
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function redirect_hooks_location {
|
|
11
|
+
# Default location for git hooks is .git/hooks
|
|
12
|
+
git config core.hooksPath scripts/hooks
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
main
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
.DEFAULT_GOAL := help
|
|
2
|
+
|
|
3
|
+
.PHONY: help
|
|
4
|
+
help: ## Show this help.
|
|
5
|
+
@grep -E '^[a-zA-Z_-]+:.*?## ' $(firstword $(MAKEFILE_LIST)) | \
|
|
6
|
+
awk 'BEGIN {FS = ":.*## "}; {printf "%-30s %s\n", $$1, $$2}'
|
|
7
|
+
|
|
8
|
+
.PHONY: test
|
|
9
|
+
test: ## Run all test.
|
|
10
|
+
@{{ dependency_manager }} run pytest -n 0 tests -ra
|
|
11
|
+
|
|
12
|
+
.PHONY: unit
|
|
13
|
+
unit: ## Run unit test in changed files.
|
|
14
|
+
@scripts/tests/unit.sh
|
|
15
|
+
|
|
16
|
+
.PHONY: integration
|
|
17
|
+
integration: ## Run integration test in changed files.
|
|
18
|
+
@scripts/tests/integration.sh
|
|
19
|
+
|
|
20
|
+
.PHONY: all-unit
|
|
21
|
+
all-unit: ## Run all unit test.
|
|
22
|
+
@{{ dependency_manager }} run pytest -n auto -m "unit" -ra
|
|
23
|
+
|
|
24
|
+
.PHONY: all-integration
|
|
25
|
+
all-integration: ## Run all integration test.
|
|
26
|
+
@{{ dependency_manager }} run pytest -m "integration" -ra
|
|
27
|
+
|
|
28
|
+
.PHONY: all-acceptance
|
|
29
|
+
all-acceptance: ## Run all acceptance test.
|
|
30
|
+
@{{ dependency_manager }} run pytest -m "acceptance" -ra
|
|
31
|
+
|
|
32
|
+
.PHONY: coverage
|
|
33
|
+
coverage: ## Run all test with coverage.
|
|
34
|
+
@{{ dependency_manager }} run coverage run --branch -m pytest tests
|
|
35
|
+
@{{ dependency_manager }} run coverage html
|
|
36
|
+
@$(BROWSER) htmlcov/index.html
|
|
37
|
+
|
|
38
|
+
.PHONY: local-setup
|
|
39
|
+
local-setup: ## Setup git hooks and install dependencies.
|
|
40
|
+
@scripts/local_setup.sh
|
|
41
|
+
@make install
|
|
42
|
+
|
|
43
|
+
.PHONY: install
|
|
44
|
+
install: ## Install dependencies.
|
|
45
|
+
{% if dependency_manager == "uv" %}
|
|
46
|
+
@uv sync --all-groups
|
|
47
|
+
{% elif dependency_manager == "pdm" %}
|
|
48
|
+
@pdm install
|
|
49
|
+
{% endif %}
|
|
50
|
+
|
|
51
|
+
.PHONY: update
|
|
52
|
+
update: ## Update dependencies.
|
|
53
|
+
{% if dependency_manager == "uv" %}
|
|
54
|
+
@uv sync --upgrade
|
|
55
|
+
{% elif dependency_manager == "pdm" %}
|
|
56
|
+
@pdm update
|
|
57
|
+
{% endif %}
|
|
58
|
+
|
|
59
|
+
.PHONY: add-dep
|
|
60
|
+
add-dep: ## Add a new dependency.
|
|
61
|
+
@scripts/add_dependency.sh
|
|
62
|
+
|
|
63
|
+
.PHONY: remove-dep
|
|
64
|
+
remove-dep: ## Remove a dependency.
|
|
65
|
+
@scripts/remove_dependency.sh
|
|
66
|
+
|
|
67
|
+
.PHONY: check-typing
|
|
68
|
+
check-typing: ## Run mypy type checking.
|
|
69
|
+
@{{ dependency_manager }} run mypy
|
|
70
|
+
|
|
71
|
+
.PHONY: check-lint
|
|
72
|
+
check-lint: ## Run ruff linting check.
|
|
73
|
+
{% if dependency_manager == "pdm" %}
|
|
74
|
+
@pdm run ruff check src tests
|
|
75
|
+
{% elif dependency_manager == "uv" %}
|
|
76
|
+
@uvx ruff check src tests
|
|
77
|
+
{% endif %}
|
|
78
|
+
|
|
79
|
+
.PHONY: lint
|
|
80
|
+
lint: ## Apply ruff linting fix.
|
|
81
|
+
{% if dependency_manager == "pdm" %}
|
|
82
|
+
@pdm run ruff check --fix src tests
|
|
83
|
+
{% elif dependency_manager == "uv" %}
|
|
84
|
+
@uvx ruff check --fix src tests
|
|
85
|
+
{% endif %}
|
|
86
|
+
|
|
87
|
+
.PHONY: check-format
|
|
88
|
+
check-format: ## Run ruff format check.
|
|
89
|
+
{% if dependency_manager == "pdm" %}
|
|
90
|
+
@pdm run ruff format --check src tests
|
|
91
|
+
{% elif dependency_manager == "uv" %}
|
|
92
|
+
@uvx ruff format --check src tests
|
|
93
|
+
{% endif %}
|
|
94
|
+
|
|
95
|
+
.PHONY: format
|
|
96
|
+
format: ## Apply ruff format fix.
|
|
97
|
+
{% if dependency_manager == "pdm" %}
|
|
98
|
+
@pdm run ruff format src tests
|
|
99
|
+
{% elif dependency_manager == "uv" %}
|
|
100
|
+
@uvx ruff format src tests
|
|
101
|
+
{% endif %}
|
|
102
|
+
|
|
103
|
+
.PHONY: pre-commit
|
|
104
|
+
pre-commit: check-typing check-lint check-format all-unit ## Run pre-commit checks.
|
|
105
|
+
|
|
106
|
+
.PHONY: pre-push
|
|
107
|
+
pre-push: all-integration all-acceptance ## Run pre-push checks.
|
|
108
|
+
|
|
109
|
+
.PHONY: watch
|
|
110
|
+
watch: ## Run all test with every change.
|
|
111
|
+
@{{ dependency_manager }} run ptw --runner "pytest -n auto tests -ra"
|
|
112
|
+
|
|
113
|
+
.PHONY: insert-template
|
|
114
|
+
insert-template: ## Insert a template class among the existing ones.
|
|
115
|
+
@{{ dependency_manager }} run python -m scripts.insert_template
|
|
116
|
+
|
|
117
|
+
.PHONY: create-aggregate
|
|
118
|
+
create-aggregate: ## Create a new aggregate inside contexts folder.
|
|
119
|
+
@{{ dependency_manager }} run python -m scripts.create_aggregate
|
|
120
|
+
|
|
121
|
+
.PHONY: show
|
|
122
|
+
show: ## Show installed dependencies.
|
|
123
|
+
{% if dependency_manager == "pdm" %}
|
|
124
|
+
@pdm list
|
|
125
|
+
{% elif dependency_manager == "uv" %}
|
|
126
|
+
@uv tree
|
|
127
|
+
{% endif %}
|
|
128
|
+
|
|
129
|
+
.PHONY: search
|
|
130
|
+
search: ## Show package details.
|
|
131
|
+
@read -p "Enter package name to search: " package;\
|
|
132
|
+
{% if dependency_manager == "pdm" %}
|
|
133
|
+
pdm show $$package
|
|
134
|
+
{% elif dependency_manager == "uv" %}
|
|
135
|
+
uv pip show $$package
|
|
136
|
+
{% endif %}
|
|
137
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# https://git-scm.com/docs/githooks#_post_merge
|
|
3
|
+
|
|
4
|
+
changed_files="$(git diff-tree -r --name-only --no-commit-id HEAD@{1} HEAD)"
|
|
5
|
+
|
|
6
|
+
detect_changes() {
|
|
7
|
+
files_to_search="Dockerfile\|pyproject.toml\|poetry.lock"
|
|
8
|
+
echo "$changed_files" | grep --quiet $files_to_search && echo " * changes detected in $1" && echo " * running $2" && make build && make install
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
detect_changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
read -p "Dependency to remove: " dependency
|
|
4
|
+
read -p "Is $dependency a dev dependency? (y/n): " is_dev
|
|
5
|
+
read -p "Does $dependency belong to a group? (y/n): " belongs_to_group
|
|
6
|
+
|
|
7
|
+
{% if dependency_manager == "pdm" -%}
|
|
8
|
+
|
|
9
|
+
dev_flag=""
|
|
10
|
+
group_flag=""
|
|
11
|
+
|
|
12
|
+
if [ "$is_dev" == "y" ]; then
|
|
13
|
+
dev_flag="-d"
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [ "$belongs_to_group" == "y" ]; then
|
|
17
|
+
read -p "Group name: " group_name
|
|
18
|
+
group_flag="-G $group_name"
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
pdm remove $dev_flag $group_flag $dependency
|
|
22
|
+
|
|
23
|
+
{%- elif dependency_manager == "uv" -%}
|
|
24
|
+
flag=""
|
|
25
|
+
|
|
26
|
+
if [ "$is_dev" == "y" ]; then
|
|
27
|
+
flag="--dev"
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if [ "$belongs_to_group" == "y" ]; then
|
|
31
|
+
read -p "Group name: " group_name
|
|
32
|
+
flag="--group $group_name"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
uv remove $flag $dependency
|
|
36
|
+
{%- endif %}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
function get_bounded_contexts_with_changes {
|
|
4
|
+
changed_files=$(git diff --name-only HEAD)
|
|
5
|
+
bounded_contexts=$(echo "$changed_files" | grep -E 'instant_python/contexts/([^/]*/)*(application|domain)' | sed -E 's|instant_python/contexts/([^/]*)/.*|\1|' | sort -u)
|
|
6
|
+
echo "$bounded_contexts"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function has_bounded_contexts {
|
|
10
|
+
local contexts="$1"
|
|
11
|
+
|
|
12
|
+
if [[ -z "$contexts" ]]; then
|
|
13
|
+
echo "No changes detected in application or domain folders of any bounded context."
|
|
14
|
+
return 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
return 0
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function run_tests {
|
|
21
|
+
local contexts="$1"
|
|
22
|
+
|
|
23
|
+
for context in $contexts; do
|
|
24
|
+
echo "Running application and domain tests for: $context"
|
|
25
|
+
application_folders=$(find tests/contexts/"$context" -type d -name "application")
|
|
26
|
+
domain_folders=$(find tests/contexts/"$context" -type d -name "domain")
|
|
27
|
+
{{ dependency_manager }} run pytest -n auto $application_folders $domain_folders -ra
|
|
28
|
+
done
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function main {
|
|
32
|
+
local bounded_contexts
|
|
33
|
+
bounded_contexts=$(get_bounded_contexts_with_changes)
|
|
34
|
+
|
|
35
|
+
if has_bounded_contexts "$bounded_contexts"; then
|
|
36
|
+
run_tests "$bounded_contexts"
|
|
37
|
+
fi
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
main
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{% set template_domain_import = "shared.domain"|compute_base_path(template) %}
|
|
2
|
+
from {{ source_name }}.{{ template_domain_import }}.exceptions.invalid_negative_value_error import (
|
|
3
|
+
InvalidNegativeValueError,
|
|
4
|
+
)
|
|
5
|
+
from {{ source_name }}.{{ template_domain_import }}.value_objects.value_object import ValueObject
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class IntValueObject(ValueObject[int]):
|
|
9
|
+
def _validate(self, value: int) -> None:
|
|
10
|
+
if value < 0:
|
|
11
|
+
raise InvalidNegativeValueError(value)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{% set template_domain_import = "shared.domain"|compute_base_path(template) %}
|
|
2
|
+
from {{ source_name }}.{{ template_domain_import }}.exceptions.incorrect_value_type_error import (
|
|
3
|
+
IncorrectValueTypeError,
|
|
4
|
+
)
|
|
5
|
+
from {{ source_name }}.{{ template_domain_import }}.exceptions.required_value_error import (
|
|
6
|
+
RequiredValueError,
|
|
7
|
+
)
|
|
8
|
+
from {{ source_name }}.{{ template_domain_import }}.value_objects.value_object import ValueObject
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StringValueObject(ValueObject[str]):
|
|
12
|
+
def __init__(self, value: str) -> None:
|
|
13
|
+
super().__init__(value)
|
|
14
|
+
|
|
15
|
+
def _validate(self, value: str) -> None:
|
|
16
|
+
if value is None:
|
|
17
|
+
raise RequiredValueError
|
|
18
|
+
if not isinstance(value, str):
|
|
19
|
+
raise IncorrectValueTypeError
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{% set template_domain_import = "shared.domain"|compute_base_path(template) %}
|
|
2
|
+
from uuid import UUID
|
|
3
|
+
|
|
4
|
+
from {{ source_name }}.{{ template_domain_import }}.exceptions.required_value_error import (
|
|
5
|
+
RequiredValueError,
|
|
6
|
+
)
|
|
7
|
+
from {{ source_name }}.{{ template_domain_import }}.value_objects.value_object import ValueObject
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Uuid(ValueObject[str]):
|
|
11
|
+
def __init__(self, value: str) -> None:
|
|
12
|
+
super().__init__(value)
|
|
13
|
+
|
|
14
|
+
def _validate(self, value: str) -> None:
|
|
15
|
+
if value is None:
|
|
16
|
+
raise RequiredValueError
|
|
17
|
+
UUID(value)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import override
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ValueObject[T](ABC):
|
|
6
|
+
_value: T
|
|
7
|
+
|
|
8
|
+
def __init__(self, value: T) -> None:
|
|
9
|
+
self._validate(value)
|
|
10
|
+
self._value = value
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def _validate(self, value: T) -> None: ...
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def value(self) -> T:
|
|
17
|
+
return self._value
|
|
18
|
+
|
|
19
|
+
@override
|
|
20
|
+
def __eq__(self, other: "ValueObject[T]") -> bool:
|
|
21
|
+
return self.value == other.value
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
- name: persistence/async/alembic
|
|
2
|
+
type: file
|
|
3
|
+
extension: .ini
|
|
4
|
+
- name: migrations
|
|
5
|
+
type: directory
|
|
6
|
+
children:
|
|
7
|
+
- name: versions
|
|
8
|
+
type: directory
|
|
9
|
+
- name: persistence/async/README
|
|
10
|
+
type: file
|
|
11
|
+
extension: .md
|
|
12
|
+
- name: persistence/async/env
|
|
13
|
+
type: file
|
|
14
|
+
extension: .py
|
|
15
|
+
- name: persistence/async/models_metadata
|
|
16
|
+
type: file
|
|
17
|
+
extension: .py
|
|
18
|
+
- name: persistence/async/script
|
|
19
|
+
type: file
|
|
20
|
+
extension: .py.mako
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
- name: persistence
|
|
2
|
+
type: directory
|
|
3
|
+
python: True
|
|
4
|
+
children:
|
|
5
|
+
- name: sqlalchemy
|
|
6
|
+
type: directory
|
|
7
|
+
python: True
|
|
8
|
+
children:
|
|
9
|
+
- name: persistence/base
|
|
10
|
+
type: file
|
|
11
|
+
extension: .py
|
|
12
|
+
- name: persistence/async/sqlalchemy_repository
|
|
13
|
+
type: file
|
|
14
|
+
extension: .py
|
|
15
|
+
- name: persistence/async/postgres_settings
|
|
16
|
+
type: file
|
|
17
|
+
extension: .py
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{% import "project_structure/macros.j2" as macros with context %}
|
|
2
|
+
root:
|
|
3
|
+
{{ macros.include_and_indent("project_structure/clean_architecture/source.yml.j2", 2) }}
|
|
4
|
+
{{ macros.include_and_indent("project_structure/clean_architecture/test.yml.j2", 2) }}
|
|
5
|
+
{% if "github_actions" in built_in_features %}
|
|
6
|
+
{{ macros.include_and_indent("project_structure/github_action.yml.j2", 2) }}
|
|
7
|
+
{% endif %}
|
|
8
|
+
{% if "makefile" in built_in_features %}
|
|
9
|
+
{{ macros.include_and_indent("project_structure/makefile.yml.j2", 2) }}
|
|
10
|
+
{% endif %}
|
|
11
|
+
{{ macros.include_and_indent("project_structure/pyproject.yml.j2", 2) }}
|
|
12
|
+
{% if git %}
|
|
13
|
+
{{ macros.include_and_indent("project_structure/gitignore.yml.j2", 2) }}
|
|
14
|
+
{% endif %}
|
|
15
|
+
{{ macros.include_and_indent("project_structure/python_version.yml.j2", 2) }}
|
|
16
|
+
{% if "pytest" in dependencies %}
|
|
17
|
+
{{ macros.include_and_indent("project_structure/pytest.yml.j2", 2) }}
|
|
18
|
+
{% endif %}
|
|
19
|
+
{% if "mypy" in dependencies %}
|
|
20
|
+
{{ macros.include_and_indent("project_structure/mypy.yml.j2", 2) }}
|
|
21
|
+
{% endif %}
|
|
22
|
+
{{ macros.include_and_indent("project_structure/license.yml.j2", 2) }}
|
|
23
|
+
{% if "async_alembic" in built_in_features %}
|
|
24
|
+
{{ macros.include_and_indent("project_structure/async_alembic.yml.j2", 2) }}
|
|
25
|
+
{% endif %}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{% import "project_structure/macros.j2" as macros with context %}
|
|
2
|
+
- name: {{ source_name }}
|
|
3
|
+
type: directory
|
|
4
|
+
python: True
|
|
5
|
+
children:
|
|
6
|
+
- name: delivery
|
|
7
|
+
type: directory
|
|
8
|
+
python: True
|
|
9
|
+
{% if "fastapi_application" in built_in_features %}
|
|
10
|
+
children:
|
|
11
|
+
{{ macros.include_and_indent("project_structure/fastapi_app.yml.j2", 8) }}
|
|
12
|
+
{% endif %}
|
|
13
|
+
- name: domain
|
|
14
|
+
type: directory
|
|
15
|
+
python: True
|
|
16
|
+
{% if ["value_objects", "event_bus"] | is_in(built_in_features) %}
|
|
17
|
+
children:
|
|
18
|
+
{% if "value_objects" in built_in_features %}
|
|
19
|
+
{{ macros.include_and_indent("project_structure/value_objects.yml.j2", 8) }}
|
|
20
|
+
{% endif %}
|
|
21
|
+
{% if "event_bus" in built_in_features %}
|
|
22
|
+
{{ macros.include_and_indent("project_structure/event_bus_domain.yml.j2", 8) }}
|
|
23
|
+
{% endif %}
|
|
24
|
+
{% endif %}
|
|
25
|
+
- name: application
|
|
26
|
+
type: directory
|
|
27
|
+
python: True
|
|
28
|
+
- name: infra
|
|
29
|
+
type: directory
|
|
30
|
+
python: True
|
|
31
|
+
{% if ["synchronous_sqlalchemy", "event_bus", "logger", "async_sqlalchemy", "async_alembic", "fastapi_application"] | is_in(built_in_features) %}
|
|
32
|
+
children:
|
|
33
|
+
{% if "synchronous_sqlalchemy" in built_in_features %}
|
|
34
|
+
{{ macros.include_and_indent("project_structure/synchronous_sqlalchemy.yml.j2", 8) }}
|
|
35
|
+
{% endif %}
|
|
36
|
+
{% if "event_bus" in built_in_features %}
|
|
37
|
+
{{ macros.include_and_indent("project_structure/event_bus_infra.yml.j2", 8) }}
|
|
38
|
+
{% endif %}
|
|
39
|
+
{% if "logger" in built_in_features %}
|
|
40
|
+
{{ macros.include_and_indent("project_structure/logger.yml.j2", 8) }}
|
|
41
|
+
{% endif %}
|
|
42
|
+
{% if "async_sqlalchemy" in built_in_features %}
|
|
43
|
+
{{ macros.include_and_indent("project_structure/async_sqlalchemy.yml.j2", 8) }}
|
|
44
|
+
{% endif %}
|
|
45
|
+
{% if "async_alembic" in built_in_features %}
|
|
46
|
+
{{ macros.include_and_indent("project_structure/alembic_migrator.yml.j2", 8) }}
|
|
47
|
+
{% endif %}
|
|
48
|
+
{% if "fastapi_application" in built_in_features %}
|
|
49
|
+
{{ macros.include_and_indent("project_structure/fastapi_infra.yml.j2", 8) }}
|
|
50
|
+
{% endif %}
|
|
51
|
+
{% endif %}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{% import "project_structure/macros.j2" as macros with context %}
|
|
2
|
+
- name: test
|
|
3
|
+
type: directory
|
|
4
|
+
python: True
|
|
5
|
+
children:
|
|
6
|
+
- name: unit
|
|
7
|
+
type: directory
|
|
8
|
+
python: True
|
|
9
|
+
{% if "event_bus" in built_in_features %}
|
|
10
|
+
children:
|
|
11
|
+
- name: event_bus/mock_event_bus
|
|
12
|
+
type: file
|
|
13
|
+
extension: .py
|
|
14
|
+
{% endif %}
|
|
15
|
+
- name: integration
|
|
16
|
+
type: directory
|
|
17
|
+
python: True
|
|
18
|
+
- name: acceptance
|
|
19
|
+
type: directory
|
|
20
|
+
python: True
|
|
21
|
+
- name: random_generator
|
|
22
|
+
type: file
|
|
23
|
+
extension: .py
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
- name: {{ bounded_context }}
|
|
2
|
+
type: directory
|
|
3
|
+
python: True
|
|
4
|
+
children:
|
|
5
|
+
- name: shared
|
|
6
|
+
type: directory
|
|
7
|
+
python: True
|
|
8
|
+
- name: {{ aggregate_name }}
|
|
9
|
+
type: directory
|
|
10
|
+
python: True
|
|
11
|
+
children:
|
|
12
|
+
- name: application
|
|
13
|
+
type: directory
|
|
14
|
+
python: True
|
|
15
|
+
- name: domain
|
|
16
|
+
type: directory
|
|
17
|
+
python: True
|
|
18
|
+
- name: infrastructure
|
|
19
|
+
type: directory
|
|
20
|
+
python: True
|