makeapp 2.1.0__tar.gz → 2.3.0__tar.gz

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 (97) hide show
  1. {makeapp-2.1.0 → makeapp-2.3.0}/LICENSE +1 -1
  2. {makeapp-2.1.0 → makeapp-2.3.0}/PKG-INFO +14 -3
  3. {makeapp-2.1.0 → makeapp-2.3.0}/README.md +11 -1
  4. {makeapp-2.1.0 → makeapp-2.3.0}/pyproject.toml +3 -15
  5. makeapp-2.3.0/src/makeapp/__init__.py +3 -0
  6. makeapp-2.3.0/src/makeapp/app_templates/__default__/.github/workflows/python-package.yml +41 -0
  7. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/pyproject.toml +2 -17
  8. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/.github/workflows/python-package.yml +5 -6
  9. makeapp-2.3.0/src/makeapp/app_templates/django/pyproject.toml +13 -0
  10. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/appmaker.py +4 -2
  11. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/apptemplate.py +5 -5
  12. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/apptools.py +31 -8
  13. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/cli.py +22 -1
  14. makeapp-2.3.0/src/makeapp/helpers/__init__.py +0 -0
  15. makeapp-2.3.0/src/makeapp/helpers/tests.py +129 -0
  16. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/helpers/vcs.py +5 -1
  17. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/helpers/venvs.py +1 -1
  18. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/rendering.py +1 -1
  19. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/utils.py +13 -29
  20. makeapp-2.1.0/src/makeapp/__init__.py +0 -3
  21. makeapp-2.1.0/src/makeapp/app_templates/__default__/tests/__init__.py +0 -12
  22. makeapp-2.1.0/src/makeapp/app_templates/django/pyproject.toml +0 -19
  23. {makeapp-2.1.0 → makeapp-2.3.0}/.gitignore +0 -0
  24. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/.gitignore +0 -0
  25. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/AUTHORS.md +0 -0
  26. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/CHANGELOG.md +0 -0
  27. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/CONTRIBUTING.md +0 -0
  28. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/INSTALL.md +0 -0
  29. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/README.md +0 -0
  30. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/docs/index.md +0 -0
  31. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/docs/quickstart.md +0 -0
  32. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/mkdocs.yml +0 -0
  33. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/ruff.toml +0 -0
  34. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/src/__package_name__/__init__.py +0 -0
  35. {makeapp-2.1.0/src/makeapp/app_templates/django/src/__package_name__/management/commands → makeapp-2.3.0/src/makeapp/app_templates/__default__/tests}/__init__.py +0 -0
  36. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/__default__/tests/test_basic.py +0 -0
  37. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/click/makeappconf.py +0 -0
  38. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/click/pyproject.toml +0 -0
  39. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/click/src/__package_name__/cli.py +0 -0
  40. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/console/pyproject.toml +0 -0
  41. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/console/src/__package_name__/cli.py +0 -0
  42. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/makeappconf.py +0 -0
  43. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/__init__.py +0 -0
  44. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/admin.py +0 -0
  45. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/apps.py +0 -0
  46. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/locale/empty +0 -0
  47. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/management/__init__.py +0 -0
  48. {makeapp-2.1.0/src/makeapp/app_templates/webscaff/src/__package_name__/settings → makeapp-2.3.0/src/makeapp/app_templates/django/src/__package_name__/management/commands}/__init__.py +0 -0
  49. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/models.py +0 -0
  50. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/templates/empty +0 -0
  51. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/templatetags/empty +0 -0
  52. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/tests/__init__.py +0 -0
  53. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/tests/conftest.py +0 -0
  54. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/tests/test_basic.py +0 -0
  55. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/django/src/__package_name__/views.py +0 -0
  56. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/pytestplugin/pyproject.toml +0 -0
  57. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/pytestplugin/src/__package_name__/entry.py +0 -0
  58. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/pytestplugin/tests/test_basic.py +0 -0
  59. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/.gitignore +0 -0
  60. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/conf/__package_name__-certbot-hook.sh +0 -0
  61. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/conf/__package_name__.service +0 -0
  62. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/conf/env_production.py +0 -0
  63. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/makeappconf.py +0 -0
  64. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/pyproject.toml +0 -0
  65. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/requirements.txt +0 -0
  66. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/src/__package_name__/core/uwsgiinit.py +0 -0
  67. {makeapp-2.1.0/src/makeapp/helpers → makeapp-2.3.0/src/makeapp/app_templates/webscaff/src/__package_name__/settings}/__init__.py +0 -0
  68. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/src/__package_name__/settings/auto.py +0 -0
  69. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/src/__package_name__/settings/base.py +0 -0
  70. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/src/__package_name__/settings/env_development.py +0 -0
  71. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/src/__package_name__/settings/env_testing.py +0 -0
  72. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/src/__package_name__/settings/sub_paths.py +0 -0
  73. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/src/__package_name__/uwsgicfg.py +0 -0
  74. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/state/certbot/empty +0 -0
  75. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/state/dumps/empty +0 -0
  76. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/state/media/empty +0 -0
  77. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/state/spool/empty +0 -0
  78. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/state/static/empty +0 -0
  79. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/tests/conftest.py +0 -0
  80. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/tests/requirements.txt +0 -0
  81. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/tests/test_module.py +0 -0
  82. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/app_templates/webscaff/wscaff.yml +0 -0
  83. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/appconfig.py +0 -0
  84. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/exceptions.py +0 -0
  85. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/helpers/dist.py +0 -0
  86. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/helpers/files.py +0 -0
  87. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/apache2 +0 -0
  88. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/apache2_src +0 -0
  89. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/bsd2cl +0 -0
  90. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/bsd3cl +0 -0
  91. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/gpl2 +0 -0
  92. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/gpl2_src +0 -0
  93. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/gpl3 +0 -0
  94. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/gpl3_src +0 -0
  95. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/mit +0 -0
  96. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/no +0 -0
  97. {makeapp-2.1.0 → makeapp-2.3.0}/src/makeapp/license_templates/no_src +0 -0
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013-2025, Igor `idle sign` Starikov
1
+ Copyright (c) 2013-2026, Igor `idle sign` Starikov
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makeapp
3
- Version: 2.1.0
3
+ Version: 2.3.0
4
4
  Summary: Simplifies routine Python application development processes.
5
5
  Project-URL: Homepage, https://github.com/idlesign/makeapp
6
6
  Project-URL: Documentation, https://makeapp.readthedocs.io/
@@ -8,9 +8,10 @@ Author-email: Igor Starikov <idlesign@yandex.ru>
8
8
  License-Expression: BSD-3-Clause
9
9
  License-File: LICENSE
10
10
  Keywords: development,scaffolding
11
- Requires-Python: >=3.10
11
+ Requires-Python: >=3.11
12
12
  Requires-Dist: click
13
13
  Requires-Dist: jinja2~=3.1
14
+ Requires-Dist: pyyaml
14
15
  Requires-Dist: requests
15
16
  Description-Content-Type: text/markdown
16
17
 
@@ -37,6 +38,7 @@ https://github.com/idlesign/makeapp
37
38
  * Easily bootstrap your development environment.
38
39
  * Build and local serve the docs.
39
40
  * Run code styling/linting.
41
+ * Run tests in different environments.
40
42
 
41
43
  ## Application scaffolding
42
44
 
@@ -111,7 +113,6 @@ Apply code style with `style` command:
111
113
  ma style
112
114
  ```
113
115
 
114
-
115
116
  ## Build/serve docs
116
117
 
117
118
  Use `docs` command:
@@ -120,6 +121,16 @@ Use `docs` command:
120
121
  ma docs
121
122
  ```
122
123
 
124
+
125
+ ## Run tests
126
+
127
+ Use `tests` command:
128
+
129
+ ``` bash
130
+ ma tests
131
+ ```
132
+
133
+
123
134
  ## Documentation
124
135
 
125
136
  https://makeapp.readthedocs.io/
@@ -21,6 +21,7 @@ https://github.com/idlesign/makeapp
21
21
  * Easily bootstrap your development environment.
22
22
  * Build and local serve the docs.
23
23
  * Run code styling/linting.
24
+ * Run tests in different environments.
24
25
 
25
26
  ## Application scaffolding
26
27
 
@@ -95,7 +96,6 @@ Apply code style with `style` command:
95
96
  ma style
96
97
  ```
97
98
 
98
-
99
99
  ## Build/serve docs
100
100
 
101
101
  Use `docs` command:
@@ -104,6 +104,16 @@ Use `docs` command:
104
104
  ma docs
105
105
  ```
106
106
 
107
+
108
+ ## Run tests
109
+
110
+ Use `tests` command:
111
+
112
+ ``` bash
113
+ ma tests
114
+ ```
115
+
116
+
107
117
  ## Documentation
108
118
 
109
119
  https://makeapp.readthedocs.io/
@@ -8,12 +8,13 @@ authors = [
8
8
  readme = "README.md"
9
9
  license = "BSD-3-Clause"
10
10
  license-files = ["LICENSE"]
11
- requires-python = ">=3.10"
11
+ requires-python = ">=3.11"
12
12
  keywords = ["scaffolding", "development"]
13
13
  dependencies = [
14
14
  "requests",
15
15
  "click",
16
16
  "jinja2~=3.1",
17
+ "pyyaml",
17
18
  ]
18
19
 
19
20
  [project.urls]
@@ -38,6 +39,7 @@ linters = [
38
39
  ]
39
40
  tests = [
40
41
  "pytest",
42
+ "pytest-datafixtures",
41
43
  ]
42
44
 
43
45
  [build-system]
@@ -75,17 +77,3 @@ exclude_also = [
75
77
  "if TYPE_CHECKING:",
76
78
  ]
77
79
 
78
- [tool.tox]
79
- skip_missing_interpreters = true
80
- env_list = [
81
- "py310",
82
- "py311",
83
- "py312",
84
- "py313",
85
- ]
86
-
87
- [tool.tox.env_run_base]
88
- dependency_groups = ["tests"]
89
- commands = [
90
- ["pytest", { replace = "posargs", default = ["tests"], extend = true }],
91
- ]
@@ -0,0 +1,3 @@
1
+
2
+ VERSION = '2.3.0'
3
+ """Application version number tuple."""
@@ -0,0 +1,41 @@
1
+ name: Python package
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ build:
11
+
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: [3.11, 3.12, 3.13, 3.14]
17
+
18
+ steps:
19
+ {% raw %}
20
+ - uses: actions/checkout@v4
21
+ - name: Set up Python ${{ matrix.python-version }}
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+ - name: Setup uv
26
+ uses: astral-sh/setup-uv@v6
27
+ - name: Install deps
28
+ run: |
29
+ uv sync --only-group tests
30
+ uv pip install coveralls
31
+ - uses: astral-sh/ruff-action@v3
32
+ with:
33
+ version: 0.13.1
34
+ args: check
35
+ - name: Run tests
36
+ env:
37
+ GITHUB_TOKEN: ${{ secrets.github_token }}
38
+ {% endraw %}
39
+ run: |
40
+ uv run coverage run -m pytest
41
+ uv run coveralls --service=github
@@ -89,26 +89,11 @@ exclude_also = [
89
89
  "if TYPE_CHECKING:",
90
90
  ]
91
91
 
92
- [tool.tox]
93
- skip_missing_interpreters = true
94
- env_list = [
95
- {% block tox_envlist %}
96
- "py310",
97
- "py311",
98
- "py312",
99
- "py313",
100
- {% endblock %}
101
- ]
102
-
103
- [tool.tox.env_run_base]
104
- dependency_groups = ["tests"]
92
+ [tool.makeapp.tests]
105
93
  deps = [
106
- {% block tox_deps %}
94
+ {% block deps_matests %}
107
95
  {% endblock %}
108
96
  ]
109
- commands = [
110
- ["pytest", { replace = "posargs", default = ["tests"], extend = true }],
111
- ]
112
97
 
113
98
  {% block tools_ext %}
114
99
  {% endblock %}
@@ -5,7 +5,6 @@ on:
5
5
  branches: [ master ]
6
6
  pull_request:
7
7
  branches: [ master ]
8
- workflow_dispatch:
9
8
 
10
9
  jobs:
11
10
  build:
@@ -14,12 +13,12 @@ jobs:
14
13
  strategy:
15
14
  fail-fast: false
16
15
  matrix:
17
- python-version: [3.11, 3.12, 3.13]
18
- django-version: [4.2, 5.1, 5.2]
16
+ python-version: [3.11, 3.12, 3.13, 3.14]
17
+ django-version: [4.2, 5.1, 5.2, 6.0]
19
18
 
20
19
  exclude:
21
- - python-version: 3.8
22
- django-version: 5.0
20
+ - python-version: 3.11
21
+ django-version: 6.0
23
22
 
24
23
  steps:
25
24
  {% raw %}
@@ -33,7 +32,7 @@ jobs:
33
32
  - name: Install deps
34
33
  run: |
35
34
  uv sync --only-group tests
36
- uv pip install coveralls "Django~=${{ matrix.django-version }}.0"
35
+ uv pip install coveralls "django~=${{ matrix.django-version }}.0"
37
36
  - uses: astral-sh/ruff-action@v3
38
37
  with:
39
38
  version: 0.13.1
@@ -0,0 +1,13 @@
1
+ {% extends parent_template %}
2
+
3
+ {% block deps_tests %}{{ super() }}
4
+ "pytest-djangoapp>=1.7.0",
5
+ {% endblock %}
6
+
7
+ {% block cov_omit %}{{ super() }}
8
+ "src/{{ package_name }}/migrations/*",
9
+ {% endblock %}
10
+
11
+ {% block deps_matests %}{{ super() }}
12
+ "django~={%raw%}${{ django-version }}{% endraw %}.0",
13
+ {% endblock %}
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import os
3
3
  import re
4
+ from contextlib import chdir
4
5
  from datetime import date
5
6
  from pathlib import Path
6
7
  from typing import Any
@@ -12,7 +13,7 @@ from .exceptions import AppMakerException
12
13
  from .helpers.vcs import VcsHelper
13
14
  from .helpers.venvs import VenvHelper
14
15
  from .rendering import Renderer
15
- from .utils import PYTHON_VERSION, chdir, configure_logging, get_user_dir, read_ini
16
+ from .utils import PYTHON_VERSION, configure_logging, get_user_dir, read_ini
16
17
 
17
18
  RE_UNKNOWN_MARKER = re.compile(r'{{ [^}]+ }}')
18
19
  BASE_PATH = os.path.dirname(__file__)
@@ -112,7 +113,7 @@ class AppMaker:
112
113
 
113
114
  # Support for user-supplied template directories.
114
115
  for template in templates_to_use or []:
115
- if '/' in template:
116
+ if os.sep in template:
116
117
  parent = str(Path(template).parent)
117
118
  if parent not in search_paths:
118
119
  search_paths.append(parent)
@@ -301,6 +302,7 @@ class AppMaker:
301
302
  :param remote_push: Whether to push to remote.
302
303
 
303
304
  """
305
+ dest = os.path.abspath(dest)
304
306
  self.logger.info(f'Application target path: {dest}')
305
307
 
306
308
  # Make remote available for hooks.
@@ -120,12 +120,12 @@ class AppTemplate:
120
120
 
121
121
  full_path = os.path.join(path, fname)
122
122
 
123
- rel_path = full_path.replace(templates_path, '').lstrip('/')
123
+ rel_path = os.path.relpath(full_path, templates_path)
124
124
 
125
125
  template_file = TemplateFile(
126
126
  template=self,
127
127
  path_full=full_path,
128
- path_rel=rel_path,
128
+ path_rel=rel_path.replace(os.sep, '/'),
129
129
  )
130
130
 
131
131
  rel_path = rel_path.replace(maker.package_dir_marker, maker.settings['package_name'])
@@ -174,9 +174,9 @@ class AppTemplate:
174
174
 
175
175
  """
176
176
  for supposed_path in search_paths:
177
- if '/' in supposed_path and os.path.exists(supposed_path):
177
+ if os.sep in supposed_path and os.path.exists(supposed_path):
178
178
  path = str(os.path.abspath(supposed_path))
179
- return path.split('/')[-1], path
179
+ return path.split(os.sep)[-1], path
180
180
 
181
181
  raise AppMakerException(
182
182
  f"Unable to find application template {name_or_path}. "
@@ -221,7 +221,7 @@ class TemplateFile:
221
221
 
222
222
  if os.path.exists(path_full):
223
223
  # Check parent file exists in template.
224
- paths.append(os.path.join(parent.name, path_rel))
224
+ paths.append(f'{parent.name}/{path_rel}')
225
225
 
226
226
  if parent.is_default:
227
227
  break
@@ -1,14 +1,17 @@
1
1
  import logging
2
2
  import os
3
+ import tomllib
4
+ from contextlib import chdir
3
5
  from datetime import datetime
4
6
  from pathlib import Path
5
7
 
6
8
  from .exceptions import ProjectorExeption
7
9
  from .helpers.dist import DistHelper
8
10
  from .helpers.files import FileHelper
11
+ from .helpers.tests import TestsHelper
9
12
  from .helpers.vcs import VcsHelper
10
13
  from .helpers.venvs import VenvHelper
11
- from .utils import chdir, configure_logging, Uv, Ruff, MkDocs
14
+ from .utils import MkDocs, Ruff, Uv, configure_logging
12
15
 
13
16
  LOG = logging.getLogger(__name__)
14
17
 
@@ -362,6 +365,7 @@ class Project:
362
365
  self.changelog: ChangelogData | None = None
363
366
  self.vcs = VcsHelper.get(project_path)
364
367
  self.venv = VenvHelper(project_path)
368
+ self._setting = {}
365
369
 
366
370
  def configure_logging(self, verbosity_lvl: int = None, format: str = '%(message)s'):
367
371
  """Switches on logging at a given level.
@@ -372,6 +376,25 @@ class Project:
372
376
  """
373
377
  configure_logging(verbosity_lvl, logger=LOG, format=format)
374
378
 
379
+ def get_settings(self) -> dict | None:
380
+ """Returns project settings from pyproject.toml (with default) or None if file not found."""
381
+
382
+ settings = self._setting
383
+
384
+ if not settings:
385
+
386
+ path = Path("pyproject.toml")
387
+ if not path.exists():
388
+ raise ProjectorExeption('No `pyproject.toml` file found in the current directory.')
389
+
390
+ with path.open("rb") as f:
391
+ data = tomllib.load(f)
392
+
393
+ settings = data.get("tool", {}).get("makeapp", {})
394
+ self._setting = settings
395
+
396
+ return settings
397
+
375
398
  def _gather_data(self):
376
399
  """Gathers data relevant for project related functions."""
377
400
 
@@ -383,11 +406,7 @@ class Project:
383
406
 
384
407
  LOG.debug(f'Gathering info from `{project_path}` directory ...')
385
408
 
386
- marker_file = 'pyproject.toml'
387
-
388
- if not os.path.isfile(marker_file):
389
- raise ProjectorExeption(f'No `{marker_file}` file found in the current directory.')
390
-
409
+ self.get_settings()
391
410
  self.vcs.check()
392
411
 
393
412
  parent_dirname = project_path.parent.name
@@ -506,6 +525,11 @@ class Project:
506
525
  self.vcs.push()
507
526
  DistHelper.upload()
508
527
 
528
+ def run_tests(self, *, only: list[str] | None = None) -> dict[str, list[str]]:
529
+ LOG.info('Running tests ...')
530
+ helper = TestsHelper(settings=self.get_settings().get('tests', {}), only=only)
531
+ return helper.run_tests()
532
+
509
533
  def style(self):
510
534
  LOG.info('Styling ...')
511
535
  Ruff.check()
@@ -523,13 +547,12 @@ class Project:
523
547
  LOG.info(f'{"Upgrading" if upgrade else "Bootstrapping"} development tools ...')
524
548
 
525
549
  tools_default = [
526
- 'tox',
527
550
  'ruff==0.13.1',
528
551
  ]
529
552
 
530
553
  method = Uv.tool_upgrade if upgrade else Uv.tool_install
531
554
 
532
- LOG.info(f'Processing uv ...')
555
+ LOG.info('Processing uv ...')
533
556
 
534
557
  if upgrade:
535
558
  Uv.upgrade()
@@ -5,13 +5,14 @@ from pathlib import Path
5
5
 
6
6
  import click
7
7
 
8
+ from makeapp.helpers.tests import TestsHelper
9
+
8
10
  from .exceptions import MakeappException
9
11
 
10
12
  try:
11
13
  from . import VERSION
12
14
  from .appmaker import AppMaker
13
15
  from .apptools import VERSION_NUMBER_CHUNKS, Project
14
- from .utils import configure_logging
15
16
 
16
17
  except MakeappException as e:
17
18
  click.secho(f'{e}', err=True, fg='red')
@@ -234,6 +235,26 @@ def style(debug):
234
235
  click.secho('Done', fg='green')
235
236
 
236
237
 
238
+ @entry_point.command()
239
+ @option_debug
240
+ @click.option('-o', '--only', multiple=True, help='Run only certain tests. E.g. -o "py314_django600"')
241
+ def tests(debug, only):
242
+ """Run tests."""
243
+ project = Project(log_level=logging.DEBUG if debug else logging.INFO)
244
+ stats = project.run_tests(only=only)
245
+
246
+ key_ok = TestsHelper.KEY_OK
247
+ key_fail = TestsHelper.KEY_FAIL
248
+
249
+ for status, color, err in ((key_ok, 'green', False), (key_fail, 'red', True)):
250
+ if items := stats[status]:
251
+ total_items = len(items)
252
+ items = ' '.join(items)
253
+ click.secho(f'Tests {status} ({total_items}):\n {items}', err=err, fg=color)
254
+
255
+ click.secho('Done', fg='green')
256
+
257
+
237
258
  @entry_point.command()
238
259
  @option_debug
239
260
  @click.option(
File without changes
@@ -0,0 +1,129 @@
1
+ import re
2
+ from functools import partial
3
+ from itertools import product
4
+ from pathlib import Path
5
+ from pprint import pformat
6
+ from sys import version_info
7
+
8
+ import yaml
9
+
10
+ from ..exceptions import CommandError
11
+ from ..utils import LOG, Uv
12
+
13
+
14
+ class TestsHelper:
15
+
16
+ _RE_VAR = re.compile(r'\$\{\{\s*([\w-]+)\s*\}\}')
17
+ _RE_VALID_IDENT = re.compile(r'[^a-zA-Z0-9]')
18
+
19
+ KEY_OK = 'OK'
20
+ KEY_FAIL = 'FAIL'
21
+
22
+ def __init__(self, *, settings: dict, only: list[str] | None = None):
23
+ self._settings = settings
24
+ self._only = set(only or [])
25
+
26
+ @classmethod
27
+ def apply_context(cls, text: str, context: dict) -> str:
28
+ """Apply context to a text (resolves variables).
29
+
30
+ :param text:
31
+ :param context:
32
+ """
33
+ def replace(match):
34
+ var_name = match.group(1)
35
+ return f'{context.get(var_name, match.group(0))}'
36
+
37
+ return cls._RE_VAR.sub(replace, text)
38
+
39
+ @classmethod
40
+ def get_matrix_github(cls, fpath: Path):
41
+ with fpath.open() as f:
42
+ config = yaml.safe_load(f)
43
+
44
+ jobs = config.get('jobs', {})
45
+ job_name = list(jobs.keys())[0]
46
+ matrix = jobs[job_name].get('strategy', {}).get('matrix', {})
47
+ exclusions = matrix.pop('exclude', [])
48
+ keys = matrix.keys()
49
+ values = matrix.values()
50
+ combinations = []
51
+
52
+ def norm(val) -> float | str:
53
+ try:
54
+ return float(val)
55
+ except (ValueError, TypeError):
56
+ return f'{val}'
57
+
58
+ for combined in product(*values):
59
+ combination = dict(zip(keys, combined, strict=False))
60
+ is_excluded = False
61
+ for exclusion in exclusions:
62
+ if all(f'{norm(combination.get(key))}' == f'{norm(val)}' for key, val in exclusion.items()):
63
+ is_excluded = True
64
+ break
65
+
66
+ if not is_excluded:
67
+ combinations.append(combination)
68
+
69
+ return combinations
70
+
71
+ def run_tests(self) -> dict[str, list[str]]:
72
+ settings = self._settings
73
+
74
+ workflow_github = Path(settings.get('workflow_github') or 'python-package.yml')
75
+ if len(workflow_github.parts) == 1:
76
+ workflow_github = Path('.github', 'workflows', workflow_github)
77
+
78
+ matrix = self.get_matrix_github(workflow_github)
79
+ apply_ctx = self.apply_context
80
+ make_valid_ident = partial(self._RE_VALID_IDENT.sub, '')
81
+ deps = settings.get('deps') or []
82
+ only = self._only
83
+
84
+ matrix_lines = '\n '.join(' '.join(f"{key}:{value}" for key, value in line.items()) for line in matrix)
85
+ LOG.info(f'Test matrix:\n {matrix_lines}')
86
+
87
+ stats = {
88
+ self.KEY_OK: [],
89
+ self.KEY_FAIL: [],
90
+ }
91
+
92
+ for combination in matrix:
93
+
94
+ python_version = combination.get('python-version') or f"{version_info.major}.{version_info.minor}"
95
+ ident_chunks = [make_valid_ident(f'py{python_version}')]
96
+ deps_resolved = []
97
+ for dep in deps:
98
+ dep = apply_ctx(dep, combination)
99
+ deps_resolved.append(f'"{dep}"')
100
+ ident_chunks.append(make_valid_ident(dep))
101
+
102
+ ident = "_".join(ident_chunks)
103
+
104
+ if not only or ident in only:
105
+ LOG.info(f'Running: {ident}: {combination} ...')
106
+
107
+ venv_dir = f'.venv_ma/{ident}'
108
+ execute = partial(Uv.exec, env={
109
+ 'VIRTUAL_ENV': venv_dir,
110
+ 'UV_PROJECT_ENVIRONMENT': venv_dir,
111
+ })
112
+ status = self.KEY_OK
113
+
114
+ try:
115
+ execute(f'sync --only-group tests --python {python_version}')
116
+
117
+ if deps_resolved := ' '.join(deps_resolved):
118
+ execute(f'pip install {deps_resolved} --python {venv_dir}')
119
+
120
+ execute('run pytest')
121
+
122
+ except CommandError:
123
+ status = self.KEY_FAIL
124
+ continue
125
+
126
+ finally:
127
+ stats[status].append(ident)
128
+
129
+ return stats
@@ -110,7 +110,11 @@ class VcsHelper:
110
110
  :param message: Commit description.
111
111
 
112
112
  """
113
- self.run_command("commit -m '%s'" % message.replace("'", "''"))
113
+ with NamedTemporaryFile() as f:
114
+ f.write(message.encode())
115
+ f.flush()
116
+
117
+ self.run_command(f'commit -F "{f.name}"')
114
118
 
115
119
  def get_remotes(self):
116
120
  """Returns a list of remotes."""
@@ -29,5 +29,5 @@ class VenvHelper:
29
29
  rmtree(f'{path}', ignore_errors=True)
30
30
 
31
31
  def register_tool(self):
32
- LOG.info(f'Registering application CLI as a tool ...')
32
+ LOG.info('Registering application CLI as a tool ...')
33
33
  Uv.tool_install('--force -e .')
@@ -1,10 +1,10 @@
1
1
  import os
2
+ from contextlib import chdir
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  from jinja2 import Environment, FileSystemLoader
5
6
 
6
7
  from .apptemplate import TemplateFile
7
- from .utils import chdir
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from .appmaker import AppMaker
@@ -49,23 +49,6 @@ def read_ini(fpath: Path) -> ConfigParser:
49
49
  return cfg
50
50
 
51
51
 
52
- @contextmanager
53
- def chdir(target_path):
54
- """Context manager.
55
-
56
- Temporarily switches the current working directory.
57
-
58
- """
59
- curr_dir = os.getcwd()
60
- os.chdir(target_path)
61
-
62
- try:
63
- yield
64
-
65
- finally:
66
- os.chdir(curr_dir)
67
-
68
-
69
52
  @contextmanager
70
53
  def temp_dir() -> Generator[str, None, None]:
71
54
  """Context manager to temporarily create a directory."""
@@ -104,10 +87,7 @@ def check_command(command: str, *, hint: str):
104
87
  :param hint:
105
88
 
106
89
  """
107
- try:
108
- run_command(f'type {command}')
109
-
110
- except CommandError:
90
+ if shutil.which(command) is None:
111
91
  raise CommandError(
112
92
  f"Failed to execute '{command}' command. "
113
93
  f"Check {hint} is installed and available.")
@@ -159,7 +139,7 @@ class Ruff:
159
139
  return run_command(f'ruff {cmd}', capture=False)
160
140
 
161
141
  @classmethod
162
- def check(cls, fix: bool = True) -> list[str]:
142
+ def check(cls, *, fix: bool = True) -> list[str]:
163
143
  return cls._run(f'check{" --fix" if fix else ""}')
164
144
 
165
145
 
@@ -183,25 +163,29 @@ class Uv:
183
163
  """Uv wrapper."""
184
164
 
185
165
  @classmethod
186
- def _run(cls, cmd: str) -> list[str]:
187
- return run_command(f'uv {cmd}', capture=False)
166
+ def exec(cls, cmd: str, env: dict | None = None) -> list[str]:
167
+ return run_command(f'uv {cmd}', env=env, capture=False)
188
168
 
189
169
  @classmethod
190
170
  def upgrade(cls) -> list[str]:
191
- return cls._run('self update')
171
+ return cls.exec('self update')
192
172
 
193
173
  @classmethod
194
174
  def tool_install(cls, name: str) -> list[str]:
195
- return cls._run(f'tool install {name}')
175
+ return cls.exec(f'tool install {name}')
196
176
 
197
177
  @classmethod
198
178
  def tool_upgrade(cls, name: str) -> list[str]:
199
- return cls._run(f'tool upgrade {name} --reinstall')
179
+ return cls.exec(f'tool upgrade {name} --reinstall')
200
180
 
201
181
  @classmethod
202
182
  def sync(cls) -> list[str]:
203
- return cls._run('sync')
183
+ return cls.exec('sync')
204
184
 
205
185
  @classmethod
206
186
  def install(cls):
207
- return run_command('curl -LsSf https://astral.sh/uv/install.sh | sh', capture=False)
187
+ if sys.platform == 'win32':
188
+ cmd = 'powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"'
189
+ else:
190
+ cmd = 'curl -LsSf https://astral.sh/uv/install.sh | sh'
191
+ return run_command(cmd, capture=False)
@@ -1,3 +0,0 @@
1
-
2
- VERSION = '2.1.0'
3
- """Application version number tuple."""
@@ -1,12 +0,0 @@
1
- import os
2
- import sys
3
- import unittest
4
-
5
- DIR_CURRENT = os.path.abspath(os.path.dirname(__file__))
6
- DIR_PARENT = os.path.dirname(DIR_CURRENT)
7
-
8
- sys.path.insert(0, DIR_PARENT)
9
-
10
- tests_loader = unittest.TestLoader()
11
- test_suit = tests_loader.discover(DIR_CURRENT)
12
- unittest.TextTestRunner().run(test_suit)
@@ -1,19 +0,0 @@
1
- {% extends parent_template %}
2
-
3
- {% block deps_tests %}{{ super() }}
4
- "pytest-djangoapp>=1.6.0",
5
- {% endblock %}
6
-
7
- {% block cov_omit %}{{ super() }}
8
- "src/{{ package_name }}/migrations/*",
9
- {% endblock %}
10
-
11
- {% block tox_envlist %}
12
- "py{310,311,312,313}-dj{42,51,52}",
13
- {% endblock %}
14
-
15
- {% block tox_deps %}{{ super() }}
16
- "dj42: Django>=4.1,<4.2",
17
- "dj51: Django>=5.1,<5.2",
18
- "dj52: Django>=5.2,<5.3",
19
- {% endblock %}
File without changes