emcd-projects 1.15rc0__tar.gz → 1.17__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.
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/.gitignore +2 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/PKG-INFO +1 -1
- emcd_projects-1.17/data/copier/answers-default.yaml +4 -0
- emcd_projects-1.17/data/copier/answers-maximum.yaml +14 -0
- emcd_projects-1.17/pyproject.toml +329 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/__/imports.py +11 -9
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/__/preparation.py +4 -3
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/__init__.py +1 -1
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/cli.py +13 -7
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/interfaces.py +44 -2
- emcd_projects-1.17/sources/emcdproj/template.py +127 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/website.py +18 -17
- emcd_projects-1.15rc0/pyproject.toml +0 -532
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/LICENSE.txt +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/data/.gitignore +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/data/templates/coverage.svg.jinja +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/data/templates/website.html.jinja +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/README.rst +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/__/__init__.py +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/__/application.py +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/__/distribution.py +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/__/state.py +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/__main__.py +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/_typedecls/__builtins__.pyi +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/exceptions.py +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/filesystem.py +0 -0
- {emcd_projects-1.15rc0 → emcd_projects-1.17}/sources/emcdproj/py.typed +0 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
project_name: python-test-maximum
|
2
|
+
distribution_name: emcdproj-maximum
|
3
|
+
package_name: maximum
|
4
|
+
description: 'All configuration options set.'
|
5
|
+
enable_rust_extension: true
|
6
|
+
include_data_resources: true
|
7
|
+
enable_property_tests: true
|
8
|
+
enable_publication: true
|
9
|
+
enable_cli: true
|
10
|
+
enable_executables: true
|
11
|
+
inject_foundations: true
|
12
|
+
inject_exceptions: true
|
13
|
+
inject_immutables: true
|
14
|
+
inject_docstring_utils: true
|
@@ -0,0 +1,329 @@
|
|
1
|
+
# vim: set filetype=toml fileencoding=utf-8:
|
2
|
+
# -*- mode: toml ; coding: utf-8 -*-
|
3
|
+
|
4
|
+
[build-system]
|
5
|
+
requires = [
|
6
|
+
'hatchling',
|
7
|
+
]
|
8
|
+
build-backend = 'hatchling.build'
|
9
|
+
|
10
|
+
[project]
|
11
|
+
name = 'emcd-projects'
|
12
|
+
description = 'Project management utilities.'
|
13
|
+
dynamic = [ 'version' ]
|
14
|
+
license = 'Apache-2.0'
|
15
|
+
readme = { 'file' = 'sources/emcdproj/README.rst', 'content-type' = 'text/x-rst' }
|
16
|
+
requires-python = '>= 3.10'
|
17
|
+
dependencies = [
|
18
|
+
'Jinja2',
|
19
|
+
'absence',
|
20
|
+
'defusedxml',
|
21
|
+
'frigid',
|
22
|
+
'icecream-truck',
|
23
|
+
'importlib-metadata', # TODO: Drop once we have separate appcore package.
|
24
|
+
'importlib-resources', # TODO: Drop once we have separate appcore package.
|
25
|
+
'packaging',
|
26
|
+
'platformdirs', # TODO: Drop once we have separate appcore package.
|
27
|
+
'tomli', # TODO: Drop once we have separate appcore package.
|
28
|
+
'typing-extensions',
|
29
|
+
# --- BEGIN: Injected by Copier ---
|
30
|
+
'tyro',
|
31
|
+
# --- END: Injected by Copier ---
|
32
|
+
]
|
33
|
+
classifiers = [ # https://pypi.org/classifiers
|
34
|
+
'Development Status :: 3 - Alpha',
|
35
|
+
'Intended Audience :: Developers',
|
36
|
+
'License :: OSI Approved :: Apache Software License',
|
37
|
+
'Programming Language :: Python :: 3 :: Only',
|
38
|
+
# --- BEGIN: Injected by Copier ---
|
39
|
+
'Programming Language :: Python :: 3.10',
|
40
|
+
'Programming Language :: Python :: 3.11',
|
41
|
+
'Programming Language :: Python :: 3.12',
|
42
|
+
'Programming Language :: Python :: 3.13',
|
43
|
+
'Programming Language :: Python :: Implementation :: CPython',
|
44
|
+
'Programming Language :: Python :: Implementation :: PyPy',
|
45
|
+
# --- END: Injected by Copier ---
|
46
|
+
'Topic :: Software Development',
|
47
|
+
]
|
48
|
+
keywords = [ 'maintenance', 'project', 'template' ]
|
49
|
+
[[project.authors]]
|
50
|
+
name = 'Eric McDonald'
|
51
|
+
email = 'emcd@users.noreply.github.com'
|
52
|
+
[project.scripts]
|
53
|
+
emcdproj = 'emcdproj:main'
|
54
|
+
[project.urls]
|
55
|
+
'Homepage' = 'https://github.com/emcd/python-project-common'
|
56
|
+
'Documentation' = 'https://emcd.github.io/python-project-common'
|
57
|
+
'Download' = 'https://pypi.org/project/emcd-projects/#files'
|
58
|
+
'Source Code' = 'https://github.com/emcd/python-project-common'
|
59
|
+
'Issue Tracker' = 'https://github.com/emcd/python-project-common/issues'
|
60
|
+
|
61
|
+
[tool.SELF]
|
62
|
+
year-of-origin = 2024
|
63
|
+
|
64
|
+
# https://coverage.readthedocs.io/en/latest/config.html
|
65
|
+
[tool.coverage.paths]
|
66
|
+
gha-runners = [
|
67
|
+
'/home/runner/work/python-project-common/python-project-common/',
|
68
|
+
'/Users/runner/work/python-project-common/python-project-common/',
|
69
|
+
'D:\a\python-project-common\python-project-common\',
|
70
|
+
]
|
71
|
+
[tool.coverage.run]
|
72
|
+
branch = true
|
73
|
+
command_line = '-m pytest' # TODO? '--fail-under'
|
74
|
+
data_file = '.auxiliary/caches/pytest/coverage.sqlite3'
|
75
|
+
parallel = true
|
76
|
+
source = [ 'sources' ]
|
77
|
+
[tool.coverage.html]
|
78
|
+
directory = '.auxiliary/artifacts/coverage-pytest'
|
79
|
+
[tool.coverage.xml]
|
80
|
+
output = '.auxiliary/artifacts/coverage-pytest/coverage.xml'
|
81
|
+
|
82
|
+
# https://hatch.pypa.io/latest/config/metadata/
|
83
|
+
[tool.hatch.build]
|
84
|
+
directory = '.auxiliary/artifacts/hatch-build'
|
85
|
+
[tool.hatch.build.targets.sdist]
|
86
|
+
only-include = [
|
87
|
+
'sources/emcdproj',
|
88
|
+
# --- BEGIN: Injected by Copier ---
|
89
|
+
'data',
|
90
|
+
# --- END: Injected by Copier ---
|
91
|
+
]
|
92
|
+
strict-naming = false
|
93
|
+
[tool.hatch.build.targets.wheel]
|
94
|
+
only-include = [
|
95
|
+
'sources/emcdproj',
|
96
|
+
# --- BEGIN: Injected by Copier ---
|
97
|
+
'data',
|
98
|
+
# --- END: Injected by Copier ---
|
99
|
+
]
|
100
|
+
strict-naming = false
|
101
|
+
[tool.hatch.build.targets.wheel.sources]
|
102
|
+
'sources/emcdproj' = 'emcdproj'
|
103
|
+
# --- BEGIN: Injected by Copier ---
|
104
|
+
'data' = 'emcdproj/data'
|
105
|
+
# --- END: Injected by Copier ---
|
106
|
+
[tool.hatch.envs.default]
|
107
|
+
python = '3.10'
|
108
|
+
[tool.hatch.envs.develop]
|
109
|
+
description = ''' Development environment. '''
|
110
|
+
dependencies = [
|
111
|
+
'Jinja2',
|
112
|
+
'coverage[toml]',
|
113
|
+
'emcd-projects',
|
114
|
+
'furo',
|
115
|
+
'icecream-truck',
|
116
|
+
'packaging',
|
117
|
+
'pre-commit',
|
118
|
+
'pyfakefs',
|
119
|
+
'pyright',
|
120
|
+
'pytest',
|
121
|
+
'pytest-asyncio',
|
122
|
+
'ruff',
|
123
|
+
'sphinx',
|
124
|
+
'sphinx-copybutton',
|
125
|
+
'sphinx-inline-tabs',
|
126
|
+
'towncrier',
|
127
|
+
# --- BEGIN: Injected by Copier ---
|
128
|
+
# --- END: Injected by Copier ---
|
129
|
+
]
|
130
|
+
post-install-commands = [
|
131
|
+
# --- BEGIN: Injected by Copier ---
|
132
|
+
# --- END: Injected by Copier ---
|
133
|
+
]
|
134
|
+
[tool.hatch.envs.develop.env-vars]
|
135
|
+
PYTHONUNBUFFERED = 'TRUE' # TODO: Only for coverage/pytest.
|
136
|
+
# --- BEGIN: Injected by Copier ---
|
137
|
+
# --- END: Injected by Copier ---
|
138
|
+
[tool.hatch.envs.develop.scripts]
|
139
|
+
docsgen = [
|
140
|
+
'''sphinx-build -E -b doctest -d .auxiliary/caches/sphinx \
|
141
|
+
documentation .auxiliary/artifacts/sphinx-doctest''',
|
142
|
+
'''sphinx-build -E -b linkcheck -d .auxiliary/caches/sphinx \
|
143
|
+
documentation .auxiliary/artifacts/sphinx-linkcheck''',
|
144
|
+
'''sphinx-build -a -d .auxiliary/caches/sphinx \
|
145
|
+
documentation .auxiliary/artifacts/sphinx-html''',
|
146
|
+
]
|
147
|
+
linters = [
|
148
|
+
'''ruff check --quiet sources documentation tests''',
|
149
|
+
# --- BEGIN: Injected by Copier ---
|
150
|
+
# --- END: Injected by Copier ---
|
151
|
+
'''pyright sources''',
|
152
|
+
]
|
153
|
+
packagers = [
|
154
|
+
'''hatch build''',
|
155
|
+
# --- BEGIN: Injected by Copier ---
|
156
|
+
# --- END: Injected by Copier ---
|
157
|
+
]
|
158
|
+
testers = [
|
159
|
+
'coverage erase',
|
160
|
+
'coverage run',
|
161
|
+
'coverage combine',
|
162
|
+
'coverage report --skip-covered',
|
163
|
+
'coverage html',
|
164
|
+
'coverage xml',
|
165
|
+
]
|
166
|
+
make-all = [
|
167
|
+
'linters',
|
168
|
+
'testers',
|
169
|
+
'packagers',
|
170
|
+
'docsgen',
|
171
|
+
]
|
172
|
+
[tool.hatch.envs.qa]
|
173
|
+
description = ''' Quality assurance environment. '''
|
174
|
+
template = 'develop'
|
175
|
+
[[tool.hatch.envs.qa.matrix]]
|
176
|
+
python = [
|
177
|
+
'3.10',
|
178
|
+
'3.11',
|
179
|
+
'3.12',
|
180
|
+
'3.13',
|
181
|
+
'pypy3.10',
|
182
|
+
]
|
183
|
+
[tool.hatch.version]
|
184
|
+
path = 'sources/emcdproj/__init__.py'
|
185
|
+
|
186
|
+
# https://mypy.readthedocs.io/en/stable/config_file.html
|
187
|
+
[tool.mypy]
|
188
|
+
# Note: Due to repeated painful experiences with Mypy, we use Pyright instead.
|
189
|
+
# Pyright properly handles TypeVars, etc...
|
190
|
+
cache_dir = '.auxiliary/caches/mypy'
|
191
|
+
exclude = [ '.*' ] # Ignore everything
|
192
|
+
ignore_errors = true
|
193
|
+
follow_imports = 'skip'
|
194
|
+
pretty = true
|
195
|
+
strict = false
|
196
|
+
|
197
|
+
# https://microsoft.github.io/pyright/#/configuration
|
198
|
+
[tool.pyright]
|
199
|
+
ignore = [ 'tests' ] # Stronger hint for language server.
|
200
|
+
include = [ 'sources' ]
|
201
|
+
reportConstantRedefinition = true
|
202
|
+
reportInvalidTypeVarUse = true
|
203
|
+
reportMatchNotExhaustive = true
|
204
|
+
reportMissingImports = true
|
205
|
+
reportMissingTypeStubs = true
|
206
|
+
reportMissingTypeArgument = true
|
207
|
+
reportPossiblyUnboundVariable = false # Covered by other linters.
|
208
|
+
reportPrivateImportUsage = false # Covered by other linters.
|
209
|
+
reportPrivateUsage = false # Covered by other linters.
|
210
|
+
reportSelfClsParameterName = false # Too opinionated.
|
211
|
+
reportUnknownArgumentType = true
|
212
|
+
reportUnknownLambdaType = true
|
213
|
+
reportUnknownMemberType = true
|
214
|
+
reportUnknownParameterType = true
|
215
|
+
reportUnknownVariableType = true
|
216
|
+
reportUnnecessaryCast = true
|
217
|
+
reportUnnecessaryComparison = true
|
218
|
+
reportUntypedBaseClass = true
|
219
|
+
reportUntypedClassDecorator = true
|
220
|
+
reportUntypedFunctionDecorator = true
|
221
|
+
reportUntypedNamedTuple = true
|
222
|
+
reportUnusedExpression = true
|
223
|
+
reportUnusedImport = false # Covered by other linters.
|
224
|
+
reportUnusedVariable = false # Covered by other linters.
|
225
|
+
#strict = [ 'sources' ]
|
226
|
+
stubPath = 'sources/emcdproj/_typedecls'
|
227
|
+
|
228
|
+
[tool.pytest.ini_options]
|
229
|
+
# Note: Cannot run doctests from Pytest, because Pytest tries to update '_'
|
230
|
+
# attribute on protected modules. Instead, we use Sphinx to run doctests.
|
231
|
+
minversion = '8.1'
|
232
|
+
addopts = '--capture=no --exitfirst -rfE'
|
233
|
+
testpaths = [ 'tests' ]
|
234
|
+
python_files = [ 'test_*.py' ]
|
235
|
+
python_functions = [ 'test_[0-9][0-9][0-9]_*' ]
|
236
|
+
cache_dir = '.auxiliary/caches/pytest'
|
237
|
+
|
238
|
+
[tool.ruff]
|
239
|
+
builtins = [ 'ictr' ]
|
240
|
+
cache-dir = '.auxiliary/caches/ruff'
|
241
|
+
indent-width = 4
|
242
|
+
line-length = 79
|
243
|
+
[tool.ruff.lint]
|
244
|
+
dummy-variable-rgx = '^_$'
|
245
|
+
ignore = [
|
246
|
+
'E701', # multiple-statements-on-one-line-colon
|
247
|
+
'PLC0415', # import-outside-top-level
|
248
|
+
'SIM300', # yoda-condition: scarred by assignment expressions, I am
|
249
|
+
]
|
250
|
+
select = [ # default: E4, E7, E9, F
|
251
|
+
'A', # Flake8 builtins rules
|
252
|
+
'B006', # mutable-argument-default
|
253
|
+
'B008', # function-call-in-default-argument
|
254
|
+
'B011', # assert-false
|
255
|
+
'B023', # function-uses-loop-variable
|
256
|
+
'B904', # raise-without-from-inside-except
|
257
|
+
'B909', # PREVIEW: loop-iterator-mutation
|
258
|
+
'C90', # McCabe complexity rules
|
259
|
+
'E101', # mixed-spaces-and-tabs
|
260
|
+
'E111', # PREVIEW: indentation-with-invalid-multiple
|
261
|
+
'E112', # PREVIEW: no-indented-block
|
262
|
+
'E4', # Pycodestyle import rules
|
263
|
+
'E501', # line-too-long
|
264
|
+
'E7', # Pycodestyle general rules
|
265
|
+
'E9', # Pycodestyle runtime errors
|
266
|
+
'F', # Pyflakes rules
|
267
|
+
'PERF', # Perflint rules
|
268
|
+
'PLC', # Pylint convention rules
|
269
|
+
'PLE', # Pylint error rules
|
270
|
+
'PLR', # Pylint refactor rules
|
271
|
+
'PLW', # Pylint warning rules
|
272
|
+
'RET', # Flake8 return rules
|
273
|
+
'RUF', # Ruff rules
|
274
|
+
'S', # Flake8 Bandit rules
|
275
|
+
'SIM', # Flake8 simplify rules
|
276
|
+
'SLF', # Flake8 self rules
|
277
|
+
'TRY', # Tryceratops rules
|
278
|
+
]
|
279
|
+
[tool.ruff.lint.mccabe]
|
280
|
+
max-complexity = 12
|
281
|
+
[tool.ruff.lint.per-file-ignores]
|
282
|
+
'__init__.py' = [
|
283
|
+
'F401', # unused-import
|
284
|
+
'F403', # undefined-local-with-import-star
|
285
|
+
'F405', # undefined-local-with-import-star-usage
|
286
|
+
]
|
287
|
+
'tests/**/*.py' = [
|
288
|
+
'PLR0124', # comparison-with-itself
|
289
|
+
'PLR0913', # too-many-arguments
|
290
|
+
'PLR0915', # too-many-statements
|
291
|
+
'PLR1704', # redefined-argument-from-local
|
292
|
+
'PLR2004', # magic-value-comparison
|
293
|
+
'PLW0129', # assert-on-string-literal
|
294
|
+
'PLW0603', # global-statement
|
295
|
+
'PLW0642', # self-assignment
|
296
|
+
'S101', # assert
|
297
|
+
'SLF001', # private-member-accessed
|
298
|
+
'TRY', # Tryceratops rules
|
299
|
+
]
|
300
|
+
[tool.ruff.lint.pylint]
|
301
|
+
max-locals = 10
|
302
|
+
max-public-methods = 10
|
303
|
+
max-statements = 30
|
304
|
+
|
305
|
+
[tool.towncrier]
|
306
|
+
directory = '.auxiliary/data/towncrier'
|
307
|
+
filename = 'documentation/changelog.rst'
|
308
|
+
package = 'emcdproj'
|
309
|
+
package_dir = 'sources'
|
310
|
+
[[tool.towncrier.type]]
|
311
|
+
# features and other improvements
|
312
|
+
directory = 'enhance'
|
313
|
+
name = 'Enhancements'
|
314
|
+
showcontent = true
|
315
|
+
[[tool.towncrier.type]]
|
316
|
+
# deprecations and other notices
|
317
|
+
directory = 'notify'
|
318
|
+
name = 'Notices'
|
319
|
+
showcontent = true
|
320
|
+
[[tool.towncrier.type]]
|
321
|
+
# removals of feature or platform support
|
322
|
+
directory = 'remove'
|
323
|
+
name = 'Removals'
|
324
|
+
showcontent = true
|
325
|
+
[[tool.towncrier.type]]
|
326
|
+
# bug fixes
|
327
|
+
directory = 'repair'
|
328
|
+
name = 'Repairs'
|
329
|
+
showcontent = true
|
@@ -20,20 +20,22 @@
|
|
20
20
|
|
21
21
|
''' Common imports and type aliases used throughout the package. '''
|
22
22
|
|
23
|
-
# pylint: disable=unused-import
|
24
23
|
# ruff: noqa: F401
|
25
24
|
|
26
25
|
|
27
26
|
from __future__ import annotations
|
28
27
|
|
29
|
-
import
|
30
|
-
import collections.abc as
|
31
|
-
import contextlib as
|
32
|
-
import
|
33
|
-
import
|
34
|
-
import
|
35
|
-
import
|
36
|
-
import
|
28
|
+
import abc
|
29
|
+
import collections.abc as cabc
|
30
|
+
import contextlib as ctxl
|
31
|
+
import enum
|
32
|
+
import io
|
33
|
+
import json
|
34
|
+
import math
|
35
|
+
import os
|
36
|
+
import shutil
|
37
|
+
import sys
|
38
|
+
import types
|
37
39
|
|
38
40
|
from pathlib import Path
|
39
41
|
|
@@ -30,9 +30,10 @@ from . import distribution as _distribution
|
|
30
30
|
from . import state as _state
|
31
31
|
|
32
32
|
|
33
|
-
async def prepare(
|
33
|
+
async def prepare(
|
34
34
|
exits: __.ctxl.AsyncExitStack,
|
35
|
-
application: _application.Information =
|
35
|
+
application: _application.Information = (
|
36
|
+
_application.Information( ) ), # noqa: B008
|
36
37
|
# configedits: _dictedits.Edits = ( ),
|
37
38
|
# configfile: __.Absential[ __.Path ] = __.absent,
|
38
39
|
# environment: bool = False,
|
@@ -66,7 +67,7 @@ async def prepare( # pylint: disable=too-many-arguments,too-many-locals
|
|
66
67
|
exits = exits )
|
67
68
|
# if environment: await _environment.update( auxdata )
|
68
69
|
# _inscribe_preparation_report( auxdata )
|
69
|
-
return auxdata
|
70
|
+
return auxdata # noqa: RET504
|
70
71
|
|
71
72
|
|
72
73
|
# def _inscribe_preparation_report( auxdata: _state.Globals ):
|
@@ -25,6 +25,7 @@ from __future__ import annotations
|
|
25
25
|
|
26
26
|
from . import __
|
27
27
|
from . import interfaces as _interfaces
|
28
|
+
from . import template as _template
|
28
29
|
from . import website as _website
|
29
30
|
|
30
31
|
|
@@ -34,8 +35,10 @@ class VersionCommand(
|
|
34
35
|
):
|
35
36
|
''' Prints version information. '''
|
36
37
|
|
37
|
-
async def __call__(
|
38
|
-
|
38
|
+
async def __call__(
|
39
|
+
self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
|
40
|
+
) -> None:
|
41
|
+
from . import __version__
|
39
42
|
print( f"{__package__} {__version__}" )
|
40
43
|
raise SystemExit( 0 )
|
41
44
|
|
@@ -48,8 +51,12 @@ class Cli(
|
|
48
51
|
|
49
52
|
application: __.ApplicationInformation
|
50
53
|
# configfile: __.typx.Optional[ str ] = None
|
51
|
-
|
54
|
+
display: _interfaces.ConsoleDisplay
|
52
55
|
command: __.typx.Union[
|
56
|
+
__.typx.Annotated[
|
57
|
+
_template.CommandDispatcher,
|
58
|
+
__.tyro.conf.subcommand( 'template', prefix_name = False ),
|
59
|
+
],
|
53
60
|
__.typx.Annotated[
|
54
61
|
_website.CommandDispatcher,
|
55
62
|
__.tyro.conf.subcommand( 'website', prefix_name = False ),
|
@@ -66,8 +73,8 @@ class Cli(
|
|
66
73
|
async with __.ctxl.AsyncExitStack( ) as exits:
|
67
74
|
auxdata = await _prepare( exits = exits, **nomargs )
|
68
75
|
ictr( 0 )( self.command )
|
69
|
-
await self.command( auxdata = auxdata )
|
70
|
-
|
76
|
+
# await self.command( auxdata = auxdata )
|
77
|
+
await self.command( auxdata = auxdata, display = self.display )
|
71
78
|
|
72
79
|
def prepare_invocation_args(
|
73
80
|
self,
|
@@ -107,9 +114,8 @@ async def _prepare(
|
|
107
114
|
import ictruck
|
108
115
|
# TODO: Finetune Icecream truck installation from CLI arguments.
|
109
116
|
ictruck.install( trace_levels = 9 )
|
110
|
-
|
117
|
+
return await __.prepare(
|
111
118
|
application = application,
|
112
119
|
# configedits = configedits,
|
113
120
|
# environment = environment,
|
114
121
|
exits = exits )
|
115
|
-
return auxdata
|
@@ -26,7 +26,47 @@ from __future__ import annotations
|
|
26
26
|
from . import __
|
27
27
|
|
28
28
|
|
29
|
-
class
|
29
|
+
class DisplayStreams( __.enum.Enum ): # TODO: Python 3.11: StrEnum
|
30
|
+
# TODO: Protected class attributes.
|
31
|
+
''' Stream upon which to place output. '''
|
32
|
+
|
33
|
+
Stderr = 'stderr'
|
34
|
+
Stdout = 'stdout'
|
35
|
+
|
36
|
+
|
37
|
+
class ConsoleDisplay(
|
38
|
+
metaclass = __.ImmutableDataclass,
|
39
|
+
):
|
40
|
+
silence: __.typx.Annotated[
|
41
|
+
bool,
|
42
|
+
__.tyro.conf.arg(
|
43
|
+
aliases = ( '--quiet', '--silent', ), prefix_name = False ),
|
44
|
+
] = False
|
45
|
+
file: __.typx.Annotated[
|
46
|
+
__.typx.Optional[ __.Path ],
|
47
|
+
__.tyro.conf.arg(
|
48
|
+
name = 'console-capture-file', prefix_name = False ),
|
49
|
+
] = None
|
50
|
+
stream: __.typx.Annotated[
|
51
|
+
DisplayStreams,
|
52
|
+
__.tyro.conf.arg( name = 'console-stream', prefix_name = False ),
|
53
|
+
] = DisplayStreams.Stderr
|
54
|
+
|
55
|
+
async def provide_stream( self ) -> __.io.TextIOWrapper:
|
56
|
+
''' Provides output stream for display. '''
|
57
|
+
# TODO: register file stream as a process-lifetime exit
|
58
|
+
if self.file: return open( self.file, 'w' )
|
59
|
+
# TODO: async context manager for async file streams
|
60
|
+
# TODO: return async stream - need async printers
|
61
|
+
# TODO: handle non-TextIOWrapper streams
|
62
|
+
match self.stream:
|
63
|
+
case DisplayStreams.Stdout:
|
64
|
+
return __.sys.stdout # pyright: ignore[reportReturnType]
|
65
|
+
case DisplayStreams.Stderr:
|
66
|
+
return __.sys.stderr # pyright: ignore[reportReturnType]
|
67
|
+
|
68
|
+
|
69
|
+
class CliCommand(
|
30
70
|
__.typx.Protocol,
|
31
71
|
metaclass = __.ImmutableProtocolDataclass,
|
32
72
|
decorators = ( __.typx.runtime_checkable, ),
|
@@ -34,7 +74,9 @@ class CliCommand( # pylint: disable=invalid-metaclass
|
|
34
74
|
''' CLI command. '''
|
35
75
|
|
36
76
|
@__.abc.abstractmethod
|
37
|
-
async def __call__(
|
77
|
+
async def __call__(
|
78
|
+
self, auxdata: __.Globals, display: ConsoleDisplay
|
79
|
+
) -> None:
|
38
80
|
''' Executes command with global state. '''
|
39
81
|
raise NotImplementedError
|
40
82
|
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# vim: set filetype=python fileencoding=utf-8:
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
#============================================================================#
|
5
|
+
# #
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); #
|
7
|
+
# you may not use this file except in compliance with the License. #
|
8
|
+
# You may obtain a copy of the License at #
|
9
|
+
# #
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0 #
|
11
|
+
# #
|
12
|
+
# Unless required by applicable law or agreed to in writing, software #
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS, #
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
15
|
+
# See the License for the specific language governing permissions and #
|
16
|
+
# limitations under the License. #
|
17
|
+
# #
|
18
|
+
#============================================================================#
|
19
|
+
|
20
|
+
|
21
|
+
''' Copier template maintenance and validation. '''
|
22
|
+
|
23
|
+
|
24
|
+
from __future__ import annotations
|
25
|
+
|
26
|
+
import subprocess as _subprocess
|
27
|
+
import tempfile as _tempfile
|
28
|
+
|
29
|
+
from . import __
|
30
|
+
from . import interfaces as _interfaces
|
31
|
+
|
32
|
+
|
33
|
+
class CommandDispatcher(
|
34
|
+
_interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
|
35
|
+
):
|
36
|
+
''' Dispatches commands for static website maintenance. '''
|
37
|
+
|
38
|
+
command: __.typx.Union[
|
39
|
+
__.typx.Annotated[
|
40
|
+
SurveyCommand,
|
41
|
+
__.tyro.conf.subcommand( 'survey', prefix_name = False ),
|
42
|
+
],
|
43
|
+
__.typx.Annotated[
|
44
|
+
ValidateCommand,
|
45
|
+
__.tyro.conf.subcommand( 'validate', prefix_name = False ),
|
46
|
+
],
|
47
|
+
]
|
48
|
+
|
49
|
+
async def __call__(
|
50
|
+
self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
|
51
|
+
) -> None:
|
52
|
+
ictr( 1 )( self.command )
|
53
|
+
await self.command( auxdata = auxdata, display = display )
|
54
|
+
|
55
|
+
|
56
|
+
class SurveyCommand(
|
57
|
+
_interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
|
58
|
+
):
|
59
|
+
''' Surveys available configuration variants. '''
|
60
|
+
|
61
|
+
async def __call__(
|
62
|
+
self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
|
63
|
+
) -> None:
|
64
|
+
stream = await display.provide_stream( )
|
65
|
+
for variant in survey_variants( auxdata ):
|
66
|
+
print( variant, file = stream )
|
67
|
+
|
68
|
+
|
69
|
+
class ValidateCommand(
|
70
|
+
_interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
|
71
|
+
):
|
72
|
+
''' Validates template against configuration variant. '''
|
73
|
+
|
74
|
+
variant: __.typx.Annotated[
|
75
|
+
str,
|
76
|
+
__.typx.Doc( ''' Configuration variant to validate. ''' ),
|
77
|
+
__.tyro.conf.Positional,
|
78
|
+
]
|
79
|
+
|
80
|
+
async def __call__(
|
81
|
+
self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
|
82
|
+
) -> None:
|
83
|
+
''' Copies new project from template for configuration variant. '''
|
84
|
+
# TODO: Validate variant argument.
|
85
|
+
validate_variant( auxdata, self.variant )
|
86
|
+
|
87
|
+
|
88
|
+
def copy_template( answers_file: __.Path, projectdir: __.Path ) -> None:
|
89
|
+
''' Copies template to target directory using answers. '''
|
90
|
+
_subprocess.run( # noqa: S603
|
91
|
+
( 'copier', 'copy', '--data-file', str( answers_file ),
|
92
|
+
'--defaults', '--overwrite', '--vcs-ref', 'HEAD',
|
93
|
+
'.', str( projectdir ) ),
|
94
|
+
cwd = __.Path( ), check = True )
|
95
|
+
|
96
|
+
|
97
|
+
def survey_variants( auxdata: __.Globals ) -> __.cabc.Sequence[ str ]:
|
98
|
+
''' Surveys available configuration variants. '''
|
99
|
+
location = auxdata.distribution.provide_data_location( 'copier' )
|
100
|
+
return tuple(
|
101
|
+
fsent.stem.lstrip( 'answers-' )
|
102
|
+
for fsent in location.glob( 'answers-*.yaml' )
|
103
|
+
if fsent.is_file( ) )
|
104
|
+
|
105
|
+
|
106
|
+
def validate_variant( auxdata: __.Globals, variant: str ) -> None:
|
107
|
+
''' Validates configuration variant. '''
|
108
|
+
answers_file = (
|
109
|
+
auxdata.distribution.provide_data_location(
|
110
|
+
'copier', f"answers-{variant}.yaml" ) )
|
111
|
+
if not answers_file.is_file( ):
|
112
|
+
# TODO: Raise error.
|
113
|
+
return
|
114
|
+
with _tempfile.TemporaryDirectory( ) as tmpdir:
|
115
|
+
projectdir = __.Path( tmpdir ) / variant
|
116
|
+
copy_template( answers_file, projectdir )
|
117
|
+
validate_variant_project( projectdir )
|
118
|
+
|
119
|
+
|
120
|
+
def validate_variant_project( projectdir: __.Path ) -> None:
|
121
|
+
''' Validates standard project as generated from template. '''
|
122
|
+
for command in (
|
123
|
+
( 'hatch', '--env', 'develop', 'run',
|
124
|
+
'python', '-m', 'pip', 'install',
|
125
|
+
'--upgrade', 'pip', 'build' ),
|
126
|
+
( 'hatch', '--env', 'develop', 'run', 'make-all' ),
|
127
|
+
): _subprocess.run( command, cwd = str( projectdir ), check = True ) # noqa: S603
|
@@ -33,8 +33,7 @@ from . import interfaces as _interfaces
|
|
33
33
|
|
34
34
|
|
35
35
|
class CommandDispatcher(
|
36
|
-
_interfaces.CliCommand,
|
37
|
-
decorators = ( __.standard_tyro_class, ),
|
36
|
+
_interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
|
38
37
|
):
|
39
38
|
''' Dispatches commands for static website maintenance. '''
|
40
39
|
|
@@ -49,25 +48,27 @@ class CommandDispatcher(
|
|
49
48
|
],
|
50
49
|
]
|
51
50
|
|
52
|
-
async def __call__(
|
51
|
+
async def __call__(
|
52
|
+
self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
|
53
|
+
) -> None:
|
53
54
|
ictr( 1 )( self.command )
|
54
|
-
await self.command( auxdata = auxdata )
|
55
|
+
await self.command( auxdata = auxdata, display = display )
|
55
56
|
|
56
57
|
|
57
58
|
class SurveyCommand(
|
58
|
-
_interfaces.CliCommand,
|
59
|
-
decorators = ( __.standard_tyro_class, ),
|
59
|
+
_interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
|
60
60
|
):
|
61
61
|
''' Surveys release versions published in static website. '''
|
62
62
|
|
63
|
-
async def __call__(
|
63
|
+
async def __call__(
|
64
|
+
self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
|
65
|
+
) -> None:
|
64
66
|
# TODO: Implement.
|
65
67
|
pass
|
66
68
|
|
67
69
|
|
68
70
|
class UpdateCommand(
|
69
|
-
_interfaces.CliCommand,
|
70
|
-
decorators = ( __.standard_tyro_class, ),
|
71
|
+
_interfaces.CliCommand, decorators = ( __.standard_tyro_class, ),
|
71
72
|
):
|
72
73
|
''' Updates static website for particular release version. '''
|
73
74
|
|
@@ -77,7 +78,9 @@ class UpdateCommand(
|
|
77
78
|
__.tyro.conf.Positional,
|
78
79
|
]
|
79
80
|
|
80
|
-
async def __call__(
|
81
|
+
async def __call__(
|
82
|
+
self, auxdata: __.Globals, display: _interfaces.ConsoleDisplay
|
83
|
+
) -> None:
|
81
84
|
update( auxdata, self.version )
|
82
85
|
|
83
86
|
|
@@ -146,7 +149,7 @@ def update(
|
|
146
149
|
locations.website.mkdir( exist_ok = True, parents = True )
|
147
150
|
if locations.archive.is_file( ):
|
148
151
|
with tarfile_open( locations.archive, 'r:xz' ) as archive:
|
149
|
-
archive.extractall( path = locations.website )
|
152
|
+
archive.extractall( path = locations.website ) # noqa: S202
|
150
153
|
available_species = _update_available_species( locations, version )
|
151
154
|
j2context = _jinja2.Environment(
|
152
155
|
loader = _jinja2.FileSystemLoader( locations.templates ),
|
@@ -157,7 +160,7 @@ def update(
|
|
157
160
|
_update_coverage_badge( locations, j2context )
|
158
161
|
( locations.website / '.nojekyll' ).touch( )
|
159
162
|
from .filesystem import chdir
|
160
|
-
with chdir( locations.website ):
|
163
|
+
with chdir( locations.website ): # noqa: SIM117
|
161
164
|
with tarfile_open( locations.archive, 'w:xz' ) as archive:
|
162
165
|
archive.add( '.' )
|
163
166
|
|
@@ -195,7 +198,7 @@ def _update_available_species(
|
|
195
198
|
return tuple( available_species )
|
196
199
|
|
197
200
|
|
198
|
-
def _update_coverage_badge(
|
201
|
+
def _update_coverage_badge(
|
199
202
|
locations: Locations, j2context: _jinja2.Environment
|
200
203
|
) -> None:
|
201
204
|
''' Updates coverage badge SVG.
|
@@ -207,11 +210,9 @@ def _update_coverage_badge( # pylint: disable=too-many-locals
|
|
207
210
|
- green: >= 80%
|
208
211
|
'''
|
209
212
|
coverage = _extract_coverage( locations )
|
210
|
-
# pylint: disable=magic-value-comparison
|
211
213
|
color = (
|
212
|
-
'red' if coverage < 50 else (
|
213
|
-
'yellow' if coverage < 80 else 'green' ) )
|
214
|
-
# pylint: enable=magic-value-comparison
|
214
|
+
'red' if coverage < 50 else ( # noqa: PLR2004
|
215
|
+
'yellow' if coverage < 80 else 'green' ) ) # noqa: PLR2004
|
215
216
|
label_text = 'coverage'
|
216
217
|
value_text = f"{coverage}%"
|
217
218
|
label_width = len( label_text ) * 6 + 10
|
@@ -1,532 +0,0 @@
|
|
1
|
-
# vim: set filetype=toml fileencoding=utf-8:
|
2
|
-
# -*- mode: toml ; coding: utf-8 -*-
|
3
|
-
|
4
|
-
[build-system]
|
5
|
-
requires = [
|
6
|
-
'hatchling',
|
7
|
-
]
|
8
|
-
build-backend = 'hatchling.build'
|
9
|
-
|
10
|
-
[project]
|
11
|
-
name = 'emcd-projects'
|
12
|
-
description = 'Project management utilities.'
|
13
|
-
dynamic = [ 'version' ]
|
14
|
-
license = 'Apache-2.0'
|
15
|
-
readme = { 'file' = 'sources/emcdproj/README.rst', 'content-type' = 'text/x-rst' }
|
16
|
-
requires-python = '>= 3.10'
|
17
|
-
dependencies = [
|
18
|
-
'Jinja2',
|
19
|
-
'absence',
|
20
|
-
'defusedxml',
|
21
|
-
'frigid',
|
22
|
-
'icecream-truck',
|
23
|
-
'importlib-metadata', # TODO: Drop once we have separate appcore package.
|
24
|
-
'importlib-resources', # TODO: Drop once we have separate appcore package.
|
25
|
-
'packaging',
|
26
|
-
'platformdirs', # TODO: Drop once we have separate appcore package.
|
27
|
-
'tomli', # TODO: Drop once we have separate appcore package.
|
28
|
-
'typing-extensions',
|
29
|
-
# --- BEGIN: Injected by Copier ---
|
30
|
-
'tyro',
|
31
|
-
# --- END: Injected by Copier ---
|
32
|
-
]
|
33
|
-
classifiers = [ # https://pypi.org/classifiers
|
34
|
-
'Development Status :: 3 - Alpha',
|
35
|
-
'Intended Audience :: Developers',
|
36
|
-
'License :: OSI Approved :: Apache Software License',
|
37
|
-
'Programming Language :: Python :: 3 :: Only',
|
38
|
-
# --- BEGIN: Injected by Copier ---
|
39
|
-
'Programming Language :: Python :: 3.10',
|
40
|
-
'Programming Language :: Python :: 3.11',
|
41
|
-
'Programming Language :: Python :: 3.12',
|
42
|
-
'Programming Language :: Python :: 3.13',
|
43
|
-
'Programming Language :: Python :: Implementation :: CPython',
|
44
|
-
'Programming Language :: Python :: Implementation :: PyPy',
|
45
|
-
# --- END: Injected by Copier ---
|
46
|
-
'Topic :: Software Development',
|
47
|
-
]
|
48
|
-
keywords = [ 'maintenance', 'project', 'template' ]
|
49
|
-
[[project.authors]]
|
50
|
-
name = 'Eric McDonald'
|
51
|
-
email = 'emcd@users.noreply.github.com'
|
52
|
-
[project.scripts]
|
53
|
-
emcdproj = 'emcdproj:main'
|
54
|
-
[project.urls]
|
55
|
-
'Homepage' = 'https://github.com/emcd/python-project-common'
|
56
|
-
'Documentation' = 'https://emcd.github.io/python-project-common'
|
57
|
-
'Download' = 'https://pypi.org/project/emcd-projects/#files'
|
58
|
-
'Source Code' = 'https://github.com/emcd/python-project-common'
|
59
|
-
'Issue Tracker' = 'https://github.com/emcd/python-project-common/issues'
|
60
|
-
|
61
|
-
[tool.SELF]
|
62
|
-
year-of-origin = 2024
|
63
|
-
|
64
|
-
[tool.bandit]
|
65
|
-
exclude_dirs = [ 'tests' ]
|
66
|
-
|
67
|
-
# https://coverage.readthedocs.io/en/latest/config.html
|
68
|
-
[tool.coverage.run]
|
69
|
-
branch = true
|
70
|
-
command_line = '-m pytest' # TODO? '--fail-under'
|
71
|
-
data_file = '.auxiliary/caches/pytest/coverage.sqlite3'
|
72
|
-
parallel = true
|
73
|
-
source = [ 'sources' ]
|
74
|
-
[tool.coverage.html]
|
75
|
-
directory = '.auxiliary/artifacts/coverage-pytest'
|
76
|
-
[tool.coverage.xml]
|
77
|
-
output = '.auxiliary/artifacts/coverage-pytest/coverage.xml'
|
78
|
-
|
79
|
-
# https://hatch.pypa.io/latest/config/metadata/
|
80
|
-
[tool.hatch.build]
|
81
|
-
directory = '.auxiliary/artifacts/hatch-build'
|
82
|
-
[tool.hatch.build.targets.sdist]
|
83
|
-
only-include = [
|
84
|
-
'sources/emcdproj',
|
85
|
-
# --- BEGIN: Injected by Copier ---
|
86
|
-
'data',
|
87
|
-
# --- END: Injected by Copier ---
|
88
|
-
]
|
89
|
-
strict-naming = false
|
90
|
-
[tool.hatch.build.targets.wheel]
|
91
|
-
only-include = [
|
92
|
-
'sources/emcdproj',
|
93
|
-
# --- BEGIN: Injected by Copier ---
|
94
|
-
'data',
|
95
|
-
# --- END: Injected by Copier ---
|
96
|
-
]
|
97
|
-
strict-naming = false
|
98
|
-
[tool.hatch.build.targets.wheel.sources]
|
99
|
-
'sources/emcdproj' = 'emcdproj'
|
100
|
-
# --- BEGIN: Injected by Copier ---
|
101
|
-
'data' = 'emcdproj/data'
|
102
|
-
# --- END: Injected by Copier ---
|
103
|
-
[tool.hatch.envs.default]
|
104
|
-
python = '3.10'
|
105
|
-
[tool.hatch.envs.develop]
|
106
|
-
description = ''' Development environment. '''
|
107
|
-
dependencies = [
|
108
|
-
'Jinja2',
|
109
|
-
'bandit',
|
110
|
-
'coverage[toml]',
|
111
|
-
'furo',
|
112
|
-
'icecream',
|
113
|
-
'isort',
|
114
|
-
'packaging',
|
115
|
-
'pre-commit',
|
116
|
-
'pyfakefs',
|
117
|
-
'pylint',
|
118
|
-
'pylint-per-file-ignores',
|
119
|
-
'pyright',
|
120
|
-
'pytest',
|
121
|
-
'pytest-asyncio',
|
122
|
-
'ruff',
|
123
|
-
'semgrep;platform_system!="Windows"', # https://github.com/returntocorp/semgrep/issues/1330
|
124
|
-
'sphinx',
|
125
|
-
'sphinx-copybutton',
|
126
|
-
'sphinx-inline-tabs',
|
127
|
-
'towncrier',
|
128
|
-
'tryceratops',
|
129
|
-
'yapf',
|
130
|
-
# --- BEGIN: Injected by Copier ---
|
131
|
-
# --- END: Injected by Copier ---
|
132
|
-
]
|
133
|
-
post-install-commands = [
|
134
|
-
# --- BEGIN: Injected by Copier ---
|
135
|
-
# --- END: Injected by Copier ---
|
136
|
-
]
|
137
|
-
[tool.hatch.envs.develop.env-vars]
|
138
|
-
PYTHONUNBUFFERED = 'TRUE' # TODO: Only for coverage/pytest.
|
139
|
-
# --- BEGIN: Injected by Copier ---
|
140
|
-
# --- END: Injected by Copier ---
|
141
|
-
[tool.hatch.envs.develop.scripts]
|
142
|
-
docsgen = [
|
143
|
-
'''sphinx-build -E -b doctest -d .auxiliary/caches/sphinx \
|
144
|
-
documentation .auxiliary/artifacts/sphinx-doctest''',
|
145
|
-
'''sphinx-build -E -b linkcheck -d .auxiliary/caches/sphinx \
|
146
|
-
documentation .auxiliary/artifacts/sphinx-linkcheck''',
|
147
|
-
'''sphinx-build -a -d .auxiliary/caches/sphinx \
|
148
|
-
documentation .auxiliary/artifacts/sphinx-html''',
|
149
|
-
]
|
150
|
-
linters = [
|
151
|
-
'''ruff check --quiet sources documentation tests''',
|
152
|
-
# --- BEGIN: Injected by Copier ---
|
153
|
-
# --- END: Injected by Copier ---
|
154
|
-
'''bandit --configfile pyproject.toml --quiet --recursive sources''',
|
155
|
-
'''tryceratops sources''',
|
156
|
-
'''pyright sources''',
|
157
|
-
'''pylint --recursive yes sources documentation tests''',
|
158
|
-
'''semgrep --config p/python --error --quiet --skip-unknown-extensions \
|
159
|
-
sources/emcdproj''',
|
160
|
-
# --- BEGIN: Injected by Copier ---
|
161
|
-
# --- END: Injected by Copier ---
|
162
|
-
]
|
163
|
-
packagers = [
|
164
|
-
'''hatch build''',
|
165
|
-
# --- BEGIN: Injected by Copier ---
|
166
|
-
# --- END: Injected by Copier ---
|
167
|
-
]
|
168
|
-
testers = [
|
169
|
-
'coverage erase',
|
170
|
-
'coverage run',
|
171
|
-
'coverage combine',
|
172
|
-
'coverage report --skip-covered',
|
173
|
-
'coverage html',
|
174
|
-
'coverage xml',
|
175
|
-
]
|
176
|
-
make-all = [
|
177
|
-
'linters',
|
178
|
-
'testers',
|
179
|
-
'packagers',
|
180
|
-
'docsgen',
|
181
|
-
]
|
182
|
-
[tool.hatch.envs.qa]
|
183
|
-
description = ''' Quality assurance environment. '''
|
184
|
-
template = 'develop'
|
185
|
-
[[tool.hatch.envs.qa.matrix]]
|
186
|
-
python = [
|
187
|
-
'3.10',
|
188
|
-
'3.11',
|
189
|
-
'3.12',
|
190
|
-
'3.13',
|
191
|
-
'pypy3.10',
|
192
|
-
]
|
193
|
-
[tool.hatch.version]
|
194
|
-
path = 'sources/emcdproj/__init__.py'
|
195
|
-
|
196
|
-
# https://pycqa.github.io/isort/docs/configuration/options.html
|
197
|
-
# https://pycqa.github.io/isort/docs/configuration/multi_line_output_modes.html
|
198
|
-
[tool.isort]
|
199
|
-
atomic = true
|
200
|
-
#color = false
|
201
|
-
#combine_as = false
|
202
|
-
#combine_straight_imports = false
|
203
|
-
#extra_standard_library = [ ]
|
204
|
-
follow_links = false
|
205
|
-
#force_alphabetical_sort_within_sections = false
|
206
|
-
#float_to_top = false
|
207
|
-
honor_noqa = true
|
208
|
-
#ignore_whitespace = false
|
209
|
-
include_trailing_comma = true
|
210
|
-
# known_OTHER: substitute OTHER
|
211
|
-
known_first_party = [ 'emcdproj' ]
|
212
|
-
#known_third_party = [ ]
|
213
|
-
#line_length = 79
|
214
|
-
#lines_after_imports = -1 # automatic
|
215
|
-
#lines_before_imports = -1 # automatic
|
216
|
-
multi_line_output = 3 # vertical hanging indent
|
217
|
-
#quiet = false
|
218
|
-
#sections = [ 'FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER' ]
|
219
|
-
skip_gitignore = true
|
220
|
-
split_on_trailing_comma = true
|
221
|
-
#src_paths = [ ]
|
222
|
-
#star_first = false
|
223
|
-
#supported_extensions = [ 'pxd', 'py', 'pyi', 'pyx' ]
|
224
|
-
use_parentheses = true
|
225
|
-
|
226
|
-
# https://mypy.readthedocs.io/en/stable/config_file.html
|
227
|
-
[tool.mypy]
|
228
|
-
# Note: Due to repeated painful experiences with Mypy, we use Pyright instead.
|
229
|
-
# Pyright properly handles TypeVars, etc...
|
230
|
-
cache_dir = '.auxiliary/caches/mypy'
|
231
|
-
exclude = [ '.*' ] # Ignore everything
|
232
|
-
ignore_errors = true
|
233
|
-
follow_imports = 'skip'
|
234
|
-
pretty = true
|
235
|
-
strict = false
|
236
|
-
|
237
|
-
# https://pylint.pycqa.org/en/latest/user_guide/configuration/index.html
|
238
|
-
[tool.pylint.main]
|
239
|
-
fail-under = 10
|
240
|
-
# TODO: jobs: Consider parallelization if output interleaving, dependency
|
241
|
-
# grouping, and similarities detection bugs have been fixed.
|
242
|
-
load-plugins = [
|
243
|
-
# 'pylint.extensions.bad_builtin',
|
244
|
-
'pylint.extensions.broad_try_clause',
|
245
|
-
'pylint.extensions.check_elif',
|
246
|
-
'pylint.extensions.code_style',
|
247
|
-
'pylint.extensions.confusing_elif',
|
248
|
-
# 'pylint.extensions.consider_ternary_expression',
|
249
|
-
'pylint.extensions.dict_init_mutate',
|
250
|
-
'pylint.extensions.dunder',
|
251
|
-
'pylint.extensions.eq_without_hash',
|
252
|
-
'pylint.extensions.for_any_all',
|
253
|
-
'pylint.extensions.magic_value',
|
254
|
-
'pylint.extensions.mccabe',
|
255
|
-
'pylint.extensions.no_self_use',
|
256
|
-
'pylint.extensions.overlapping_exceptions',
|
257
|
-
'pylint.extensions.private_import',
|
258
|
-
'pylint.extensions.redefined_loop_name',
|
259
|
-
'pylint.extensions.redefined_variable_type',
|
260
|
-
# 'pylint.extensions.set_membership',
|
261
|
-
# 'pylint.extensions.while_used',
|
262
|
-
'pylint_per_file_ignores',
|
263
|
-
]
|
264
|
-
recursive = false
|
265
|
-
suggestion-mode = true
|
266
|
-
[tool.pylint.basic]
|
267
|
-
const-rgx = '([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$'
|
268
|
-
good-names = [ '_', '__' ]
|
269
|
-
include-naming-hint = true
|
270
|
-
[tool.pylint.broad_try_clause]
|
271
|
-
max-try-statements = 2
|
272
|
-
[tool.pylint.classes]
|
273
|
-
defining-attr-methods = [ '__init__', '__new__', '__post_init__' ]
|
274
|
-
exclude-protected = [ ]
|
275
|
-
valid-classmethod-first-arg = [ 'selfclass' ]
|
276
|
-
valid-metaclass-classmethod-first-arg = [ 'clscls' ]
|
277
|
-
[tool.pylint.design]
|
278
|
-
# TODO: exclude-too-few-public-methods: Consider exception hierarchies.
|
279
|
-
ignored-parents = [
|
280
|
-
'abc.ABCMeta',
|
281
|
-
'builtins.AttributeError',
|
282
|
-
'builtins.BaseException',
|
283
|
-
'builtins.Exception',
|
284
|
-
'builtins.KeyError',
|
285
|
-
'builtins.TypeError',
|
286
|
-
'builtins.ValueError',
|
287
|
-
'builtins.type',
|
288
|
-
'typing.Protocol',
|
289
|
-
'typing_extensions.Protocol',
|
290
|
-
# --- BEGIN: Injected by Copier ---
|
291
|
-
'emcdproj.exceptions.Omnierror',
|
292
|
-
'emcdproj.exceptions.Omniexception',
|
293
|
-
# --- END: Injected by Copier ---
|
294
|
-
]
|
295
|
-
max-args = 5
|
296
|
-
max-attributes = 7
|
297
|
-
max-bool-expr = 3
|
298
|
-
max-branches = 12
|
299
|
-
max-locals = 10
|
300
|
-
max-parents = 3
|
301
|
-
max-public-methods = 10
|
302
|
-
max-returns = 6
|
303
|
-
max-statements = 30
|
304
|
-
min-public-methods = 1
|
305
|
-
[tool.pylint.exceptions]
|
306
|
-
overgeneral-exceptions = [
|
307
|
-
'builtins.BaseException',
|
308
|
-
'builtins.Exception',
|
309
|
-
# --- BEGIN: Injected by Copier ---
|
310
|
-
'emcdproj.exceptions.Omnierror',
|
311
|
-
'emcdproj.exceptions.Omniexception',
|
312
|
-
# --- END: Injected by Copier ---
|
313
|
-
]
|
314
|
-
[tool.pylint.format]
|
315
|
-
ignore-long-lines = '''^(\s*<?https?://\S+>?|([^'"]*\s+)?#.*)$'''
|
316
|
-
max-line-length = 79
|
317
|
-
max-module-lines = 800
|
318
|
-
single-line-class-stmt = true
|
319
|
-
single-line-if-stmt = true
|
320
|
-
[tool.pylint.imports]
|
321
|
-
allow-wildcard-with-all = false
|
322
|
-
# TODO: ext-import-graph
|
323
|
-
# TODO: int-import-graph
|
324
|
-
[tool.pylint.logging]
|
325
|
-
logging-format-style = 'new'
|
326
|
-
[tool.pylint.'messages control']
|
327
|
-
disable = [
|
328
|
-
'bad-dunder-name',
|
329
|
-
'consider-using-assignment-expr',
|
330
|
-
'consider-using-f-string',
|
331
|
-
'duplicate-code', # TODO: Re-enable after heuristic is fixed.
|
332
|
-
'fixme',
|
333
|
-
'f-string-without-interpolation',
|
334
|
-
'import-outside-toplevel',
|
335
|
-
'invalid-name',
|
336
|
-
'logging-format-interpolation',
|
337
|
-
'logging-fstring-interpolation',
|
338
|
-
'multiple-statements',
|
339
|
-
'reimported',
|
340
|
-
'too-few-public-methods',
|
341
|
-
'too-many-positional-arguments', # 'too-many-arguments' is good enough
|
342
|
-
'ungrouped-imports',
|
343
|
-
'unused-wildcard-import',
|
344
|
-
'use-dict-literal',
|
345
|
-
'using-exception-groups-in-unsupported-version',
|
346
|
-
'wrong-import-order',
|
347
|
-
'wrong-import-position',
|
348
|
-
]
|
349
|
-
# TODO: Latest 'per-file-ignores' code may supports dicts and lists in addition to strings.
|
350
|
-
per-file-ignores = '''
|
351
|
-
/tests/:attribute-defined-outside-init,comparison-with-itself,magic-value-comparison,missing-class-docstring,missing-function-docstring,protected-access,redefined-outer-name,singleton-comparison,superfluous-parens,too-many-try-statements,unexpected-keyword-arg
|
352
|
-
__init__\.py:unused-import
|
353
|
-
''' # Note: Paths are regexes.
|
354
|
-
[tool.pylint.refactoring]
|
355
|
-
max-nested-blocks = 3
|
356
|
-
never-returning-functions = [ 'sys.exit', 'argparse.parse_error' ]
|
357
|
-
[tool.pylint.reports]
|
358
|
-
msg-template = '{path} {line:3d},{column:2d} [{symbol}] {msg}'
|
359
|
-
output-format = 'colorized'
|
360
|
-
reports = false
|
361
|
-
score = true
|
362
|
-
[tool.pylint.similarities]
|
363
|
-
ignore-comments = true
|
364
|
-
ignore-docstrings = true
|
365
|
-
ignore-imports = true
|
366
|
-
ignore-signatures = true
|
367
|
-
min-similarity-lines = 5
|
368
|
-
[tool.pylint.spelling]
|
369
|
-
max-spelling-suggestions = 4
|
370
|
-
# TODO: spelling-dict
|
371
|
-
[tool.pylint.typecheck]
|
372
|
-
contextmanager-decorators = [ 'contextlib.contextmanager' ]
|
373
|
-
ignored-checks-for-mixins = [
|
374
|
-
'attribute-defined-outside-init',
|
375
|
-
'no-member',
|
376
|
-
'not-async-context-manager',
|
377
|
-
'not-context-manager',
|
378
|
-
]
|
379
|
-
#ignored-classes = [ 'thread._local', '_thread._local', 'argparse.Namespace' ]
|
380
|
-
ignore-mixin-members = false
|
381
|
-
missing-member-hint = true
|
382
|
-
missing-member-hint-distance = 1
|
383
|
-
missing-member-max-choices = 2
|
384
|
-
#mixin-class-rgx = '.*[Mm]ixin'
|
385
|
-
#signature-mutators = [ ]
|
386
|
-
[tool.pylint.variables]
|
387
|
-
additional-builtins = [ 'ictr' ]
|
388
|
-
callbacks = [ ]
|
389
|
-
dummy-variables-rgx = '''_$'''
|
390
|
-
ignored-argument-names = '''_.*'''
|
391
|
-
redefining-builtins-modules = [ 'builtins', 'io' ]
|
392
|
-
|
393
|
-
# https://microsoft.github.io/pyright/#/configuration
|
394
|
-
[tool.pyright]
|
395
|
-
include = [ 'sources' ]
|
396
|
-
ignore = [ 'tests' ]
|
397
|
-
reportConstantRedefinition = true
|
398
|
-
reportInvalidTypeVarUse = true
|
399
|
-
reportMatchNotExhaustive = true
|
400
|
-
reportMissingImports = true
|
401
|
-
reportMissingTypeStubs = true
|
402
|
-
reportMissingTypeArgument = true
|
403
|
-
reportPossiblyUnboundVariable = false # Covered by other linters.
|
404
|
-
reportPrivateImportUsage = false # Covered by other linters.
|
405
|
-
reportPrivateUsage = false # Covered by other linters.
|
406
|
-
reportSelfClsParameterName = false # Too opinionated.
|
407
|
-
reportUnknownArgumentType = true
|
408
|
-
reportUnknownLambdaType = true
|
409
|
-
reportUnknownMemberType = true
|
410
|
-
reportUnknownParameterType = true
|
411
|
-
reportUnknownVariableType = true
|
412
|
-
reportUnnecessaryCast = true
|
413
|
-
reportUnnecessaryComparison = true
|
414
|
-
reportUntypedBaseClass = true
|
415
|
-
reportUntypedClassDecorator = true
|
416
|
-
reportUntypedFunctionDecorator = true
|
417
|
-
reportUntypedNamedTuple = true
|
418
|
-
reportUnusedExpression = true
|
419
|
-
reportUnusedImport = false # Covered by other linters.
|
420
|
-
reportUnusedVariable = false # Covered by other linters.
|
421
|
-
#strict = [ 'sources' ]
|
422
|
-
stubPath = 'sources/emcdproj/_typedecls'
|
423
|
-
|
424
|
-
[tool.pytest.ini_options]
|
425
|
-
# Note: Cannot run doctests from Pytest, because Pytest tries to update '_'
|
426
|
-
# attribute on protected modules. Instead, we use Sphinx to run doctests.
|
427
|
-
minversion = '8.1'
|
428
|
-
addopts = '--capture=no --exitfirst -rfE'
|
429
|
-
testpaths = [ 'tests' ]
|
430
|
-
python_files = [ 'test_*.py' ]
|
431
|
-
python_functions = [ 'test_[0-9][0-9][0-9]_*' ]
|
432
|
-
cache_dir = '.auxiliary/caches/pytest'
|
433
|
-
|
434
|
-
[tool.ruff]
|
435
|
-
builtins = [ 'ictr' ]
|
436
|
-
cache-dir = '.auxiliary/caches/ruff'
|
437
|
-
[tool.ruff.lint]
|
438
|
-
ignore = [
|
439
|
-
'E701', # multiple-statements-on-one-line-colon
|
440
|
-
]
|
441
|
-
[tool.ruff.lint.per-file-ignores]
|
442
|
-
'__init__.py' = [
|
443
|
-
'F401', # unused-import
|
444
|
-
'F403', # undefined-local-with-import-star
|
445
|
-
'F405', # undefined-local-with-import-star-usage
|
446
|
-
]
|
447
|
-
|
448
|
-
[tool.towncrier]
|
449
|
-
directory = '.auxiliary/data/towncrier'
|
450
|
-
filename = 'documentation/changelog.rst'
|
451
|
-
package = 'emcdproj'
|
452
|
-
package_dir = 'sources'
|
453
|
-
[[tool.towncrier.type]]
|
454
|
-
directory = 'bugfix'
|
455
|
-
name = 'Bugfixes'
|
456
|
-
showcontent = true
|
457
|
-
[[tool.towncrier.type]]
|
458
|
-
directory = 'docs'
|
459
|
-
name = 'Documentation Improvements'
|
460
|
-
showcontent = true
|
461
|
-
[[tool.towncrier.type]]
|
462
|
-
directory = 'feature'
|
463
|
-
name = 'Features'
|
464
|
-
showcontent = true
|
465
|
-
[[tool.towncrier.type]]
|
466
|
-
directory = 'platform'
|
467
|
-
name = 'Supported Platforms'
|
468
|
-
showcontent = true
|
469
|
-
[[tool.towncrier.type]]
|
470
|
-
directory = 'removal'
|
471
|
-
name = 'Deprecations and Removals'
|
472
|
-
showcontent = true
|
473
|
-
|
474
|
-
[tool.tryceratops]
|
475
|
-
exclude = [ 'tests' ]
|
476
|
-
|
477
|
-
# https://github.com/google/yapf?tab=readme-ov-file#knobs
|
478
|
-
# yapf --style-help
|
479
|
-
[tool.yapf]
|
480
|
-
align_closing_bracket_with_visual_indent = false
|
481
|
-
allow_multiline_dictionary_keys = true
|
482
|
-
allow_multiline_lambdas = true
|
483
|
-
#allow_split_before_default_or_named_assigns = true
|
484
|
-
#allow_split_before_dict_value = true
|
485
|
-
blank_line_before_module_docstring = true
|
486
|
-
#blank_line_before_nested_class_or_def = true
|
487
|
-
#blank_lines_around_top_level_definition = 2
|
488
|
-
blank_lines_between_top_level_imports_and_variables = 2
|
489
|
-
#coalesce_brackets = false
|
490
|
-
#column_limit = 79
|
491
|
-
#continuation_align_style = 'SPACE'
|
492
|
-
#continuation_indent_width = 4
|
493
|
-
dedent_closing_brackets = true
|
494
|
-
#disable_ending_comma_heuristic = false
|
495
|
-
#disable_split_list_with_comment = false
|
496
|
-
#each_dict_entry_on_separate_line = true
|
497
|
-
#force_multiline_dict = false
|
498
|
-
indent_dictionary_value = true
|
499
|
-
#indent_width = 4
|
500
|
-
join_multiple_lines = true
|
501
|
-
#space_between_ending_comma_and_closing_bracket = true
|
502
|
-
space_inside_brackets = true
|
503
|
-
spaces_around_default_or_named_assign = true
|
504
|
-
spaces_around_dict_delimiters = true
|
505
|
-
spaces_around_list_delimiters = true
|
506
|
-
spaces_around_power_operator = true
|
507
|
-
spaces_around_subscript_colon = true
|
508
|
-
spaces_around_tuple_delimiters = true
|
509
|
-
#spaces_before_comment = 2
|
510
|
-
#split_all_comma_separated_values = false
|
511
|
-
#split_all_top_level_comma_separated_values = false
|
512
|
-
split_arguments_when_comma_terminated = true
|
513
|
-
split_before_arithmetic_operator = true
|
514
|
-
split_before_bitwise_operator = true
|
515
|
-
split_before_closing_bracket = false
|
516
|
-
#split_before_dict_set_generator = true
|
517
|
-
split_before_dot = true
|
518
|
-
split_before_expression_after_opening_paren = true
|
519
|
-
split_before_first_argument = true
|
520
|
-
split_before_logical_operator = true
|
521
|
-
#split_before_named_assigns = true
|
522
|
-
split_complex_comprehension = true
|
523
|
-
split_penalty_after_opening_bracket = 100 # default: 300
|
524
|
-
#split_penalty_after_unary_operator = 10000
|
525
|
-
#split_penalty_arithmetic_operator = 300
|
526
|
-
#split_penalty_before_if_expr = 0
|
527
|
-
#split_penalty_bitwise_operator = 300
|
528
|
-
#split_penalty_comprehension = 80
|
529
|
-
#split_penalty_excess_character = 7000
|
530
|
-
#split_penalty_for_added_line_split = 30
|
531
|
-
#split_penalty_import_names = 0
|
532
|
-
#split_penalty_logical_operator = 300
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|