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.
Files changed (149) hide show
  1. instant_python/__init__.py +0 -0
  2. instant_python/cli.py +11 -0
  3. instant_python/folder_cli.py +50 -0
  4. instant_python/installer/__init__.py +0 -0
  5. instant_python/installer/dependency_manager.py +15 -0
  6. instant_python/installer/dependency_manager_factory.py +14 -0
  7. instant_python/installer/git_configurer.py +47 -0
  8. instant_python/installer/installer.py +18 -0
  9. instant_python/installer/managers.py +7 -0
  10. instant_python/installer/operating_systems.py +7 -0
  11. instant_python/installer/pdm_manager.py +72 -0
  12. instant_python/installer/uv_manager.py +73 -0
  13. instant_python/project_cli.py +100 -0
  14. instant_python/project_generator/__init__.py +0 -0
  15. instant_python/project_generator/custom_template_manager.py +20 -0
  16. instant_python/project_generator/default_template_manager.py +45 -0
  17. instant_python/project_generator/directory.py +28 -0
  18. instant_python/project_generator/file.py +20 -0
  19. instant_python/project_generator/folder_tree.py +40 -0
  20. instant_python/project_generator/jinja_custom_filters.py +18 -0
  21. instant_python/project_generator/node.py +14 -0
  22. instant_python/project_generator/project_generator.py +31 -0
  23. instant_python/project_generator/template_manager.py +7 -0
  24. instant_python/question_prompter/__init__.py +0 -0
  25. instant_python/question_prompter/question/__init__.py +0 -0
  26. instant_python/question_prompter/question/boolean_question.py +13 -0
  27. instant_python/question_prompter/question/choice_question.py +18 -0
  28. instant_python/question_prompter/question/conditional_question.py +25 -0
  29. instant_python/question_prompter/question/dependencies_question.py +43 -0
  30. instant_python/question_prompter/question/free_text_question.py +13 -0
  31. instant_python/question_prompter/question/multiple_choice_question.py +13 -0
  32. instant_python/question_prompter/question/question.py +15 -0
  33. instant_python/question_prompter/question_wizard.py +15 -0
  34. instant_python/question_prompter/step/__init__.py +0 -0
  35. instant_python/question_prompter/step/dependencies_step.py +20 -0
  36. instant_python/question_prompter/step/general_custom_template_project_step.py +45 -0
  37. instant_python/question_prompter/step/general_project_step.py +50 -0
  38. instant_python/question_prompter/step/git_step.py +23 -0
  39. instant_python/question_prompter/step/steps.py +16 -0
  40. instant_python/question_prompter/step/template_step.py +63 -0
  41. instant_python/question_prompter/template_types.py +7 -0
  42. instant_python/question_prompter/user_requirements.py +39 -0
  43. instant_python/templates/__init__.py +0 -0
  44. instant_python/templates/boilerplate/.gitignore +164 -0
  45. instant_python/templates/boilerplate/.pre-commit-config.yml +33 -0
  46. instant_python/templates/boilerplate/.python-version +1 -0
  47. instant_python/templates/boilerplate/LICENSE +896 -0
  48. instant_python/templates/boilerplate/event_bus/__init__.py +0 -0
  49. instant_python/templates/boilerplate/event_bus/aggregate_root.py +19 -0
  50. instant_python/templates/boilerplate/event_bus/domain_event.py +15 -0
  51. instant_python/templates/boilerplate/event_bus/domain_event_json_deserializer.py +28 -0
  52. instant_python/templates/boilerplate/event_bus/domain_event_json_serializer.py +17 -0
  53. instant_python/templates/boilerplate/event_bus/domain_event_subscriber.py +15 -0
  54. instant_python/templates/boilerplate/event_bus/event_bus.py +10 -0
  55. instant_python/templates/boilerplate/event_bus/exchange_type.py +7 -0
  56. instant_python/templates/boilerplate/event_bus/mock_event_bus.py +18 -0
  57. instant_python/templates/boilerplate/event_bus/rabbit_mq_configurer.py +54 -0
  58. instant_python/templates/boilerplate/event_bus/rabbit_mq_connection.py +77 -0
  59. instant_python/templates/boilerplate/event_bus/rabbit_mq_consumer.py +58 -0
  60. instant_python/templates/boilerplate/event_bus/rabbit_mq_event_bus.py +28 -0
  61. instant_python/templates/boilerplate/event_bus/rabbit_mq_queue_formatter.py +22 -0
  62. instant_python/templates/boilerplate/event_bus/rabbit_mq_settings.py +8 -0
  63. instant_python/templates/boilerplate/exceptions/__init__.py +0 -0
  64. instant_python/templates/boilerplate/exceptions/domain_error.py +17 -0
  65. instant_python/templates/boilerplate/exceptions/domain_event_type_not_found_error.py +17 -0
  66. instant_python/templates/boilerplate/exceptions/incorrect_value_type_error.py +21 -0
  67. instant_python/templates/boilerplate/exceptions/invalid_id_format_error.py +17 -0
  68. instant_python/templates/boilerplate/exceptions/invalid_negative_value_error.py +17 -0
  69. instant_python/templates/boilerplate/exceptions/required_value_error.py +17 -0
  70. instant_python/templates/boilerplate/fastapi/__init__.py +0 -0
  71. instant_python/templates/boilerplate/fastapi/application.py +25 -0
  72. instant_python/templates/boilerplate/fastapi/http_response.py +45 -0
  73. instant_python/templates/boilerplate/fastapi/lifespan.py +14 -0
  74. instant_python/templates/boilerplate/fastapi/status_code.py +9 -0
  75. instant_python/templates/boilerplate/github/action.yml +22 -0
  76. instant_python/templates/boilerplate/github/test_lint.yml +36 -0
  77. instant_python/templates/boilerplate/logger/__init__.py +0 -0
  78. instant_python/templates/boilerplate/logger/json_formatter.py +16 -0
  79. instant_python/templates/boilerplate/logger/logger.py +39 -0
  80. instant_python/templates/boilerplate/mypy.ini +41 -0
  81. instant_python/templates/boilerplate/persistence/__init__.py +0 -0
  82. instant_python/templates/boilerplate/persistence/alembic_migrator.py +20 -0
  83. instant_python/templates/boilerplate/persistence/async/README.md +1 -0
  84. instant_python/templates/boilerplate/persistence/async/__init__.py +0 -0
  85. instant_python/templates/boilerplate/persistence/async/alembic.ini +124 -0
  86. instant_python/templates/boilerplate/persistence/async/async_engine_fixture.py +21 -0
  87. instant_python/templates/boilerplate/persistence/async/env.py +95 -0
  88. instant_python/templates/boilerplate/persistence/async/models_metadata.py +11 -0
  89. instant_python/templates/boilerplate/persistence/async/postgres_settings.py +15 -0
  90. instant_python/templates/boilerplate/persistence/async/script.py.mako +26 -0
  91. instant_python/templates/boilerplate/persistence/async/sqlalchemy_repository.py +30 -0
  92. instant_python/templates/boilerplate/persistence/base.py +4 -0
  93. instant_python/templates/boilerplate/persistence/synchronous/__init__.py +0 -0
  94. instant_python/templates/boilerplate/persistence/synchronous/session_maker.py +22 -0
  95. instant_python/templates/boilerplate/persistence/synchronous/sqlalchemy_repository.py +35 -0
  96. instant_python/templates/boilerplate/pyproject.toml +29 -0
  97. instant_python/templates/boilerplate/pytest.ini +10 -0
  98. instant_python/templates/boilerplate/random_generator.py +9 -0
  99. instant_python/templates/boilerplate/scripts/add_dependency.sh +37 -0
  100. instant_python/templates/boilerplate/scripts/create_aggregate.py +33 -0
  101. instant_python/templates/boilerplate/scripts/insert_template.py +90 -0
  102. instant_python/templates/boilerplate/scripts/integration.sh +39 -0
  103. instant_python/templates/boilerplate/scripts/local_setup.sh +15 -0
  104. instant_python/templates/boilerplate/scripts/makefile +137 -0
  105. instant_python/templates/boilerplate/scripts/post-merge +11 -0
  106. instant_python/templates/boilerplate/scripts/pre-commit +4 -0
  107. instant_python/templates/boilerplate/scripts/pre-push +6 -0
  108. instant_python/templates/boilerplate/scripts/remove_dependency.sh +36 -0
  109. instant_python/templates/boilerplate/scripts/unit.sh +40 -0
  110. instant_python/templates/boilerplate/value_object/__init__.py +0 -0
  111. instant_python/templates/boilerplate/value_object/int_value_object.py +11 -0
  112. instant_python/templates/boilerplate/value_object/string_value_object.py +19 -0
  113. instant_python/templates/boilerplate/value_object/uuid.py +17 -0
  114. instant_python/templates/boilerplate/value_object/value_object.py +21 -0
  115. instant_python/templates/project_structure/alembic_migrator.yml.j2 +3 -0
  116. instant_python/templates/project_structure/async_alembic.yml.j2 +20 -0
  117. instant_python/templates/project_structure/async_sqlalchemy.yml.j2 +17 -0
  118. instant_python/templates/project_structure/clean_architecture/main_structure.yml.j2 +25 -0
  119. instant_python/templates/project_structure/clean_architecture/source.yml.j2 +51 -0
  120. instant_python/templates/project_structure/clean_architecture/test.yml.j2 +23 -0
  121. instant_python/templates/project_structure/domain_driven_design/bounded_context.yml.j2 +20 -0
  122. instant_python/templates/project_structure/domain_driven_design/main_structure.yml.j2 +25 -0
  123. instant_python/templates/project_structure/domain_driven_design/source.yml.j2 +55 -0
  124. instant_python/templates/project_structure/domain_driven_design/test.yml.j2 +26 -0
  125. instant_python/templates/project_structure/event_bus_domain.yml.j2 +26 -0
  126. instant_python/templates/project_structure/event_bus_infra.yml.j2 +32 -0
  127. instant_python/templates/project_structure/fastapi_app.yml.j2 +10 -0
  128. instant_python/templates/project_structure/fastapi_infra.yml.j2 +10 -0
  129. instant_python/templates/project_structure/github_action.yml.j2 +18 -0
  130. instant_python/templates/project_structure/gitignore.yml.j2 +2 -0
  131. instant_python/templates/project_structure/license.yml.j2 +2 -0
  132. instant_python/templates/project_structure/logger.yml.j2 +10 -0
  133. instant_python/templates/project_structure/macros.j2 +6 -0
  134. instant_python/templates/project_structure/makefile.yml.j2 +38 -0
  135. instant_python/templates/project_structure/mypy.yml.j2 +3 -0
  136. instant_python/templates/project_structure/pre_commit.yml.j2 +3 -0
  137. instant_python/templates/project_structure/pyproject.yml.j2 +3 -0
  138. instant_python/templates/project_structure/pytest.yml.j2 +3 -0
  139. instant_python/templates/project_structure/python_version.yml.j2 +2 -0
  140. instant_python/templates/project_structure/standard_project/main_structure.yml.j2 +25 -0
  141. instant_python/templates/project_structure/standard_project/source.yml.j2 +30 -0
  142. instant_python/templates/project_structure/standard_project/test.yml.j2 +16 -0
  143. instant_python/templates/project_structure/synchronous_sqlalchemy.yml.j2 +17 -0
  144. instant_python/templates/project_structure/value_objects.yml.j2 +35 -0
  145. instant_python-0.0.1.dist-info/METADATA +276 -0
  146. instant_python-0.0.1.dist-info/RECORD +149 -0
  147. instant_python-0.0.1.dist-info/WHEEL +4 -0
  148. instant_python-0.0.1.dist-info/entry_points.txt +2 -0
  149. 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,4 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ make pre-commit
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ exec < /dev/tty
5
+
6
+ make pre-push
@@ -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
@@ -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,3 @@
1
+ - name: persistence/alembic_migrator
2
+ type: file
3
+ extension: .py
@@ -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