pyrig 2.2.6__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 (102) hide show
  1. pyrig/__init__.py +1 -0
  2. pyrig/dev/__init__.py +6 -0
  3. pyrig/dev/builders/__init__.py +1 -0
  4. pyrig/dev/builders/base/__init__.py +5 -0
  5. pyrig/dev/builders/base/base.py +256 -0
  6. pyrig/dev/builders/pyinstaller.py +229 -0
  7. pyrig/dev/cli/__init__.py +5 -0
  8. pyrig/dev/cli/cli.py +95 -0
  9. pyrig/dev/cli/commands/__init__.py +1 -0
  10. pyrig/dev/cli/commands/build_artifacts.py +16 -0
  11. pyrig/dev/cli/commands/create_root.py +25 -0
  12. pyrig/dev/cli/commands/create_tests.py +244 -0
  13. pyrig/dev/cli/commands/init_project.py +160 -0
  14. pyrig/dev/cli/commands/make_inits.py +27 -0
  15. pyrig/dev/cli/commands/protect_repo.py +145 -0
  16. pyrig/dev/cli/shared_subcommands.py +20 -0
  17. pyrig/dev/cli/subcommands.py +73 -0
  18. pyrig/dev/configs/__init__.py +1 -0
  19. pyrig/dev/configs/base/__init__.py +5 -0
  20. pyrig/dev/configs/base/base.py +826 -0
  21. pyrig/dev/configs/containers/__init__.py +1 -0
  22. pyrig/dev/configs/containers/container_file.py +111 -0
  23. pyrig/dev/configs/dot_env.py +95 -0
  24. pyrig/dev/configs/dot_python_version.py +88 -0
  25. pyrig/dev/configs/git/__init__.py +5 -0
  26. pyrig/dev/configs/git/gitignore.py +181 -0
  27. pyrig/dev/configs/git/pre_commit.py +170 -0
  28. pyrig/dev/configs/licence.py +112 -0
  29. pyrig/dev/configs/markdown/__init__.py +1 -0
  30. pyrig/dev/configs/markdown/docs/__init__.py +1 -0
  31. pyrig/dev/configs/markdown/docs/index.py +38 -0
  32. pyrig/dev/configs/markdown/readme.py +132 -0
  33. pyrig/dev/configs/py_typed.py +28 -0
  34. pyrig/dev/configs/pyproject.py +436 -0
  35. pyrig/dev/configs/python/__init__.py +5 -0
  36. pyrig/dev/configs/python/builders_init.py +27 -0
  37. pyrig/dev/configs/python/configs_init.py +28 -0
  38. pyrig/dev/configs/python/dot_experiment.py +46 -0
  39. pyrig/dev/configs/python/main.py +59 -0
  40. pyrig/dev/configs/python/resources_init.py +27 -0
  41. pyrig/dev/configs/python/shared_subcommands.py +29 -0
  42. pyrig/dev/configs/python/src_init.py +27 -0
  43. pyrig/dev/configs/python/subcommands.py +27 -0
  44. pyrig/dev/configs/testing/__init__.py +5 -0
  45. pyrig/dev/configs/testing/conftest.py +64 -0
  46. pyrig/dev/configs/testing/fixtures_init.py +27 -0
  47. pyrig/dev/configs/testing/main_test.py +74 -0
  48. pyrig/dev/configs/testing/zero_test.py +43 -0
  49. pyrig/dev/configs/workflows/__init__.py +5 -0
  50. pyrig/dev/configs/workflows/base/__init__.py +5 -0
  51. pyrig/dev/configs/workflows/base/base.py +1662 -0
  52. pyrig/dev/configs/workflows/build.py +106 -0
  53. pyrig/dev/configs/workflows/health_check.py +133 -0
  54. pyrig/dev/configs/workflows/publish.py +68 -0
  55. pyrig/dev/configs/workflows/release.py +90 -0
  56. pyrig/dev/tests/__init__.py +5 -0
  57. pyrig/dev/tests/conftest.py +40 -0
  58. pyrig/dev/tests/fixtures/__init__.py +1 -0
  59. pyrig/dev/tests/fixtures/assertions.py +147 -0
  60. pyrig/dev/tests/fixtures/autouse/__init__.py +5 -0
  61. pyrig/dev/tests/fixtures/autouse/class_.py +42 -0
  62. pyrig/dev/tests/fixtures/autouse/module.py +40 -0
  63. pyrig/dev/tests/fixtures/autouse/session.py +589 -0
  64. pyrig/dev/tests/fixtures/factories.py +118 -0
  65. pyrig/dev/utils/__init__.py +1 -0
  66. pyrig/dev/utils/cli.py +17 -0
  67. pyrig/dev/utils/git.py +312 -0
  68. pyrig/dev/utils/packages.py +93 -0
  69. pyrig/dev/utils/resources.py +77 -0
  70. pyrig/dev/utils/testing.py +66 -0
  71. pyrig/dev/utils/versions.py +268 -0
  72. pyrig/main.py +9 -0
  73. pyrig/py.typed +0 -0
  74. pyrig/resources/GITIGNORE +216 -0
  75. pyrig/resources/LATEST_PYTHON_VERSION +1 -0
  76. pyrig/resources/MIT_LICENSE_TEMPLATE +21 -0
  77. pyrig/resources/__init__.py +1 -0
  78. pyrig/src/__init__.py +1 -0
  79. pyrig/src/git/__init__.py +6 -0
  80. pyrig/src/git/git.py +146 -0
  81. pyrig/src/graph.py +255 -0
  82. pyrig/src/iterate.py +107 -0
  83. pyrig/src/modules/__init__.py +22 -0
  84. pyrig/src/modules/class_.py +369 -0
  85. pyrig/src/modules/function.py +189 -0
  86. pyrig/src/modules/inspection.py +148 -0
  87. pyrig/src/modules/module.py +658 -0
  88. pyrig/src/modules/package.py +452 -0
  89. pyrig/src/os/__init__.py +6 -0
  90. pyrig/src/os/os.py +121 -0
  91. pyrig/src/project/__init__.py +5 -0
  92. pyrig/src/project/mgt.py +83 -0
  93. pyrig/src/resource.py +58 -0
  94. pyrig/src/string.py +100 -0
  95. pyrig/src/testing/__init__.py +6 -0
  96. pyrig/src/testing/assertions.py +66 -0
  97. pyrig/src/testing/convention.py +203 -0
  98. pyrig-2.2.6.dist-info/METADATA +174 -0
  99. pyrig-2.2.6.dist-info/RECORD +102 -0
  100. pyrig-2.2.6.dist-info/WHEEL +4 -0
  101. pyrig-2.2.6.dist-info/entry_points.txt +3 -0
  102. pyrig-2.2.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,436 @@
1
+ """Configuration management for pyproject.toml.
2
+
3
+ This module provides the PyprojectConfigFile class for managing the
4
+ project's pyproject.toml file. It handles project metadata, dependencies,
5
+ tool configurations (ruff, mypy, pytest, bandit), and build settings.
6
+
7
+ The configuration enforces pyrig's opinionated defaults:
8
+ - All ruff rules enabled (with minimal exceptions)
9
+ - Strict mypy type checking
10
+ - Bandit security scanning
11
+ - uv as the build backend
12
+ """
13
+
14
+ import re
15
+ from functools import cache
16
+ from pathlib import Path
17
+ from subprocess import CompletedProcess # nosec: B404
18
+ from typing import Any, Literal
19
+
20
+ import requests
21
+ from packaging.version import Version
22
+
23
+ from pyrig.dev.cli.commands.init_project import STANDARD_DEV_DEPS
24
+ from pyrig.dev.configs.base.base import TomlConfigFile
25
+ from pyrig.dev.utils.resources import return_resource_content_on_fetch_error
26
+ from pyrig.dev.utils.versions import VersionConstraint, adjust_version_to_level
27
+ from pyrig.src.git.git import get_repo_owner_and_name_from_git
28
+ from pyrig.src.modules.package import (
29
+ get_pkg_name_from_cwd,
30
+ get_pkg_name_from_project_name,
31
+ get_project_name_from_cwd,
32
+ )
33
+ from pyrig.src.os.os import run_subprocess
34
+ from pyrig.src.testing.convention import (
35
+ COVERAGE_THRESHOLD,
36
+ TESTS_PACKAGE_NAME,
37
+ )
38
+
39
+
40
+ class PyprojectConfigFile(TomlConfigFile):
41
+ """Configuration file manager for pyproject.toml.
42
+
43
+ Manages the central project configuration including:
44
+ - Project metadata (name, description, dependencies)
45
+ - Build system configuration (uv)
46
+ - Tool configurations (ruff, mypy, pytest, bandit)
47
+ - CLI entry points
48
+
49
+ The class provides utilities for querying project information
50
+ and managing dependencies.
51
+ """
52
+
53
+ @classmethod
54
+ def dump(cls, config: dict[str, Any] | list[Any]) -> None:
55
+ """Write configuration to pyproject.toml.
56
+
57
+ Normalizes dependencies before writing.
58
+
59
+ Args:
60
+ config: The configuration dict to write.
61
+
62
+ Raises:
63
+ TypeError: If config is not a dict.
64
+ """
65
+ if not isinstance(config, dict):
66
+ msg = f"Cannot dump {config} to pyproject.toml file."
67
+ raise TypeError(msg)
68
+ # remove the versions from the dependencies
69
+ cls.remove_wrong_dependencies(config)
70
+ super().dump(config)
71
+
72
+ @classmethod
73
+ def get_parent_path(cls) -> Path:
74
+ """Get the project root directory.
75
+
76
+ Returns:
77
+ Path to the project root.
78
+ """
79
+ return Path()
80
+
81
+ @classmethod
82
+ def get_configs(cls) -> dict[str, Any]:
83
+ """Get the expected pyproject.toml configuration.
84
+
85
+ Returns:
86
+ Complete configuration dict with project metadata,
87
+ dependencies, build system, and tool configurations.
88
+ """
89
+ from pyrig.dev.cli import ( # noqa: PLC0415
90
+ cli,
91
+ )
92
+
93
+ repo_owner, _ = get_repo_owner_and_name_from_git(check_repo_url=False)
94
+
95
+ return {
96
+ "project": {
97
+ "name": get_project_name_from_cwd(),
98
+ "version": cls.get_project_version(),
99
+ "description": cls.get_project_description(),
100
+ "readme": "README.md",
101
+ "authors": [
102
+ {"name": repo_owner},
103
+ ],
104
+ "license-files": ["LICENSE"],
105
+ "requires-python": cls.get_project_requires_python(),
106
+ "classifiers": [
107
+ *cls.make_python_version_classifiers(),
108
+ ],
109
+ "scripts": {
110
+ cls.get_project_name(): f"{cli.__name__}:{cli.main.__name__}"
111
+ },
112
+ "dependencies": cls.make_dependency_versions(cls.get_dependencies()),
113
+ },
114
+ "dependency-groups": {
115
+ "dev": cls.make_dependency_versions(
116
+ cls.get_dev_dependencies(),
117
+ additional=cls.get_standard_dev_dependencies(),
118
+ )
119
+ },
120
+ "build-system": {
121
+ "requires": ["uv_build"],
122
+ "build-backend": "uv_build",
123
+ },
124
+ "tool": {
125
+ "uv": {
126
+ "build-backend": {
127
+ "module-name": get_pkg_name_from_cwd(),
128
+ "module-root": "",
129
+ }
130
+ },
131
+ "ruff": {
132
+ "exclude": [".*", "**/migrations/*.py"],
133
+ "lint": {
134
+ "select": ["ALL"],
135
+ "ignore": ["D203", "D213", "COM812", "ANN401"],
136
+ "fixable": ["ALL"],
137
+ "per-file-ignores": {
138
+ f"**/{TESTS_PACKAGE_NAME}/**/*.py": ["S101"],
139
+ },
140
+ "pydocstyle": {"convention": "google"},
141
+ },
142
+ },
143
+ "mypy": {
144
+ "strict": True,
145
+ "warn_unreachable": True,
146
+ "show_error_codes": True,
147
+ "files": ".",
148
+ },
149
+ "pytest": {
150
+ "ini_options": {
151
+ "testpaths": [TESTS_PACKAGE_NAME],
152
+ "addopts": f"--cov={cls.get_package_name()} --cov-report=term-missing --cov-fail-under={COVERAGE_THRESHOLD}", # noqa: E501
153
+ }
154
+ },
155
+ "bandit": {
156
+ "exclude_dirs": [
157
+ ".*",
158
+ ],
159
+ "assert_used": {
160
+ "skips": [
161
+ f"*/{TESTS_PACKAGE_NAME}/*.py",
162
+ ],
163
+ },
164
+ },
165
+ },
166
+ }
167
+
168
+ @classmethod
169
+ def remove_wrong_dependencies(cls, config: dict[str, Any]) -> None:
170
+ """Normalize dependencies by removing version specifiers.
171
+
172
+ Args:
173
+ config: The configuration dict to modify in place.
174
+ """
175
+ # removes the versions from the dependencies
176
+ config["project"]["dependencies"] = cls.make_dependency_versions(
177
+ config["project"]["dependencies"]
178
+ )
179
+ config["dependency-groups"]["dev"] = cls.make_dependency_versions(
180
+ config["dependency-groups"]["dev"]
181
+ )
182
+
183
+ @classmethod
184
+ def get_project_description(cls) -> str:
185
+ """Get the project description from pyproject.toml.
186
+
187
+ Returns:
188
+ The project description or empty string.
189
+ """
190
+ return str(cls.load().get("project", {}).get("description", ""))
191
+
192
+ @classmethod
193
+ def get_project_version(cls) -> str:
194
+ """Get the project version from pyproject.toml.
195
+
196
+ Returns:
197
+ The project version or empty string.
198
+ """
199
+ return str(cls.load().get("project", {}).get("version", ""))
200
+
201
+ @classmethod
202
+ def make_python_version_classifiers(cls) -> list[str]:
203
+ """Make the Python version classifiers.
204
+
205
+ Returns:
206
+ List of Python version classifiers.
207
+ """
208
+ versions = cls.get_supported_python_versions()
209
+ return [
210
+ f"Programming Language :: Python :: {v.major}.{v.minor}" for v in versions
211
+ ]
212
+
213
+ @classmethod
214
+ def get_project_requires_python(cls, default: str = ">=3.12") -> str:
215
+ """Get the project's requires-python from pyproject.toml.
216
+
217
+ Returns:
218
+ The project's requires-python or empty string.
219
+ """
220
+ return str(cls.load().get("project", {}).get("requires-python", default))
221
+
222
+ @classmethod
223
+ def make_dependency_versions(
224
+ cls,
225
+ dependencies: list[str],
226
+ additional: list[str] | None = None,
227
+ ) -> list[str]:
228
+ """Normalize and merge dependency lists.
229
+
230
+ Args:
231
+ dependencies: Primary dependencies to process.
232
+ additional: Additional dependencies to merge.
233
+
234
+ Returns:
235
+ Sorted, deduplicated list of normalized dependencies.
236
+ """
237
+ if additional is None:
238
+ additional = []
239
+ # remove all versions from the dependencies to compare them
240
+ stripped_dependencies = {
241
+ cls.remove_version_from_dep(dep) for dep in dependencies
242
+ }
243
+ additional = [
244
+ dep
245
+ for dep in additional
246
+ if cls.remove_version_from_dep(dep) not in stripped_dependencies
247
+ ]
248
+ dependencies.extend(additional)
249
+ return sorted(set(dependencies))
250
+
251
+ @classmethod
252
+ def remove_version_from_dep(cls, dep: str) -> str:
253
+ """Strip version specifier from a dependency string.
254
+
255
+ Args:
256
+ dep: Dependency string like "requests>=2.0".
257
+
258
+ Returns:
259
+ Package name without version (e.g., "requests").
260
+ """
261
+ return re.split(r"[^a-zA-Z0-9_.-]", dep)[0]
262
+
263
+ @classmethod
264
+ def get_package_name(cls) -> str:
265
+ """Get the Python package name (with underscores).
266
+
267
+ Returns:
268
+ The package name derived from the project name.
269
+ """
270
+ project_name = cls.get_project_name()
271
+ return get_pkg_name_from_project_name(project_name)
272
+
273
+ @classmethod
274
+ def get_project_name(cls) -> str:
275
+ """Get the project name from pyproject.toml.
276
+
277
+ Returns:
278
+ The project name or empty string.
279
+ """
280
+ return str(cls.load().get("project", {}).get("name", ""))
281
+
282
+ @classmethod
283
+ def get_all_dependencies(cls) -> list[str]:
284
+ """Get all dependencies (runtime and dev).
285
+
286
+ Returns:
287
+ Combined list of all dependencies.
288
+ """
289
+ all_deps = cls.get_dependencies()
290
+ all_deps.extend(cls.get_dev_dependencies())
291
+ return all_deps
292
+
293
+ @classmethod
294
+ def get_standard_dev_dependencies(cls) -> list[str]:
295
+ """Get pyrig's standard development dependencies.
296
+
297
+ Returns:
298
+ Sorted list of standard dev dependencies.
299
+ """
300
+ # sort the dependencies
301
+ return sorted(STANDARD_DEV_DEPS)
302
+
303
+ @classmethod
304
+ def get_dev_dependencies(cls) -> list[str]:
305
+ """Get development dependencies from pyproject.toml.
306
+
307
+ Returns:
308
+ List of dev dependencies.
309
+ """
310
+ dev_deps: list[str] = cls.load().get("dependency-groups", {}).get("dev", [])
311
+ return dev_deps
312
+
313
+ @classmethod
314
+ def get_dependencies(cls) -> list[str]:
315
+ """Get runtime dependencies from pyproject.toml.
316
+
317
+ Returns:
318
+ List of runtime dependencies.
319
+ """
320
+ deps: list[str] = cls.load().get("project", {}).get("dependencies", [])
321
+ return deps
322
+
323
+ @classmethod
324
+ @return_resource_content_on_fetch_error(resource_name="LATEST_PYTHON_VERSION")
325
+ @cache
326
+ def fetch_latest_python_version(cls) -> str:
327
+ """Fetch the latest stable Python version from endoflife.date.
328
+
329
+ Returns:
330
+ The latest stable Python version.
331
+
332
+ Raises:
333
+ requests.HTTPError: If the API request fails.
334
+ """
335
+ url = "https://endoflife.date/api/python.json"
336
+ resp = requests.get(url, timeout=10)
337
+ resp.raise_for_status()
338
+ data: list[dict[str, str]] = resp.json()
339
+ # first element has metadata for latest stable
340
+ return data[0]["latest"]
341
+
342
+ @classmethod
343
+ def get_latest_python_version(
344
+ cls, level: Literal["major", "minor", "micro"] = "minor"
345
+ ) -> Version:
346
+ """Fetch the latest stable Python version from endoflife.date.
347
+
348
+ Returns:
349
+ The latest stable Python version as a string.
350
+
351
+ Raises:
352
+ requests.HTTPError: If the API request fails.
353
+ """
354
+ latest_version = Version(cls.fetch_latest_python_version())
355
+ return adjust_version_to_level(latest_version, level)
356
+
357
+ @classmethod
358
+ def get_latest_possible_python_version(
359
+ cls, level: Literal["major", "minor", "micro"] = "micro"
360
+ ) -> Version:
361
+ """Get the latest Python version allowed by requires-python.
362
+
363
+ Args:
364
+ level: Version precision (major, minor, or micro).
365
+
366
+ Returns:
367
+ The latest allowed Python version.
368
+ """
369
+ constraint = cls.load()["project"]["requires-python"]
370
+ version_constraint = VersionConstraint(constraint)
371
+ version = version_constraint.get_upper_inclusive()
372
+ if version is None:
373
+ version = cls.get_latest_python_version()
374
+
375
+ return adjust_version_to_level(version, level)
376
+
377
+ @classmethod
378
+ def get_first_supported_python_version(cls) -> Version:
379
+ """Get the minimum supported Python version.
380
+
381
+ Returns:
382
+ The minimum Python version from requires-python.
383
+
384
+ Raises:
385
+ ValueError: If no lower bound is specified.
386
+ """
387
+ constraint = cls.get_project_requires_python()
388
+ version_constraint = VersionConstraint(constraint)
389
+ lower = version_constraint.get_lower_inclusive()
390
+ if lower is None:
391
+ msg = "Need a lower bound for python version"
392
+ raise ValueError(msg)
393
+ return lower
394
+
395
+ @classmethod
396
+ def get_supported_python_versions(cls) -> list[Version]:
397
+ """Get all supported Python minor versions.
398
+
399
+ Returns:
400
+ List of supported Python versions (e.g., [3.10, 3.11, 3.12]).
401
+ """
402
+ constraint = cls.get_project_requires_python()
403
+ version_constraint = VersionConstraint(constraint)
404
+ return version_constraint.get_version_range(
405
+ level="minor", upper_default=cls.get_latest_python_version()
406
+ )
407
+
408
+ @classmethod
409
+ def update_dependencies(cls, *, check: bool = True) -> CompletedProcess[bytes]:
410
+ """Update dependencies to their latest versions.
411
+
412
+ Args:
413
+ check: Whether to raise on non-zero exit code.
414
+
415
+ Returns:
416
+ The completed process result.
417
+ """
418
+ from pyrig.src.project.mgt import PROJECT_MGT # noqa: PLC0415
419
+
420
+ upgrade_deps = run_subprocess([PROJECT_MGT, "lock", "--upgrade"], check=check)
421
+ _ = cls.install_dependencies(check=check)
422
+ return upgrade_deps
423
+
424
+ @classmethod
425
+ def install_dependencies(cls, *, check: bool = True) -> CompletedProcess[bytes]:
426
+ """Install project dependencies using uv sync.
427
+
428
+ Args:
429
+ check: Whether to raise on non-zero exit code.
430
+
431
+ Returns:
432
+ The completed process result.
433
+ """
434
+ from pyrig.src.project.mgt import PROJECT_MGT # noqa: PLC0415
435
+
436
+ return run_subprocess([PROJECT_MGT, "sync"], check=check)
@@ -0,0 +1,5 @@
1
+ """Python source file configuration management.
2
+
3
+ This package provides ConfigFile subclasses for creating and managing
4
+ Python source files like __init__.py, main.py, and subcommands.py.
5
+ """
@@ -0,0 +1,27 @@
1
+ """Configuration for the builders package __init__.py.
2
+
3
+ This module provides the BuildersInitConfigFile class for creating
4
+ the dev/artifacts/builders directory structure with an __init__.py file.
5
+ """
6
+
7
+ from types import ModuleType
8
+
9
+ from pyrig.dev import builders
10
+ from pyrig.dev.configs.base.base import InitConfigFile
11
+
12
+
13
+ class BuildersInitConfigFile(InitConfigFile):
14
+ """Configuration file manager for builders/__init__.py.
15
+
16
+ Creates the dev/artifacts/builders directory with an __init__.py
17
+ file that mirrors pyrig's builders package structure.
18
+ """
19
+
20
+ @classmethod
21
+ def get_src_module(cls) -> ModuleType:
22
+ """Get the source module to mirror.
23
+
24
+ Returns:
25
+ The pyrig.dev.builders module.
26
+ """
27
+ return builders
@@ -0,0 +1,28 @@
1
+ """Configuration for the configs package __init__.py.
2
+
3
+ This module provides the ConfigsInitConfigFile class for creating
4
+ the dev/configs directory structure with an __init__.py file.
5
+ All ConfigFile subclasses in this package are automatically discovered.
6
+ """
7
+
8
+ from types import ModuleType
9
+
10
+ from pyrig.dev import configs
11
+ from pyrig.dev.configs.base.base import InitConfigFile
12
+
13
+
14
+ class ConfigsInitConfigFile(InitConfigFile):
15
+ """Configuration file manager for configs/__init__.py.
16
+
17
+ Creates the dev/configs directory with an __init__.py file
18
+ that mirrors pyrig's configs package structure.
19
+ """
20
+
21
+ @classmethod
22
+ def get_src_module(cls) -> ModuleType:
23
+ """Get the source module to mirror.
24
+
25
+ Returns:
26
+ The pyrig.dev.configs module.
27
+ """
28
+ return configs
@@ -0,0 +1,46 @@
1
+ """Configuration for the .experiment.py scratch file.
2
+
3
+ This module provides the DotExperimentConfigFile class for creating
4
+ a .experiment.py file at the project root for local experimentation.
5
+ This file is automatically added to .gitignore.
6
+ """
7
+
8
+ from pathlib import Path
9
+
10
+ from pyrig.dev.configs.base.base import PythonConfigFile
11
+
12
+
13
+ class DotExperimentConfigFile(PythonConfigFile):
14
+ """Configuration file manager for .experiment.py.
15
+
16
+ Creates a scratch Python file at the project root for local
17
+ experimentation. This file is excluded from version control.
18
+ """
19
+
20
+ @classmethod
21
+ def get_filename(cls) -> str:
22
+ """Get the experiment filename.
23
+
24
+ Returns:
25
+ The string ".experiment".
26
+ """
27
+ return ".experiment"
28
+
29
+ @classmethod
30
+ def get_parent_path(cls) -> Path:
31
+ """Get the project root directory.
32
+
33
+ Returns:
34
+ Path to the project root.
35
+ """
36
+ return Path()
37
+
38
+ @classmethod
39
+ def get_content_str(cls) -> str:
40
+ """Get the experiment file content.
41
+
42
+ Returns:
43
+ A minimal Python file with a docstring.
44
+ """
45
+ return '''"""This file is for experimentation and is ignored by git."""
46
+ '''
@@ -0,0 +1,59 @@
1
+ """Configuration for the main.py entry point.
2
+
3
+ This module provides the MainConfigFile class for creating the
4
+ main.py file in the package's src directory. This file serves
5
+ as the CLI entry point.
6
+ """
7
+
8
+ from pathlib import Path
9
+ from types import ModuleType
10
+
11
+ from pyrig import main
12
+ from pyrig.dev.configs.base.base import CopyModuleConfigFile
13
+
14
+
15
+ class MainConfigFile(CopyModuleConfigFile):
16
+ """Configuration file manager for main.py.
17
+
18
+ Creates a main.py in pkg_name/src that serves as the CLI entry point.
19
+ Also cleans up any root-level main.py files.
20
+ """
21
+
22
+ def __init__(self) -> None:
23
+ """Initialize and clean up any root-level main.py."""
24
+ super().__init__()
25
+ self.__class__.delete_root_main()
26
+
27
+ @classmethod
28
+ def get_src_module(cls) -> ModuleType:
29
+ """Get the source module to copy.
30
+
31
+ Returns:
32
+ The pyrig.main module.
33
+ """
34
+ return main
35
+
36
+ @classmethod
37
+ def is_correct(cls) -> bool:
38
+ """Check if the main.py file is valid.
39
+
40
+ Allows modifications as long as the file contains a main function
41
+ and the standard __name__ == '__main__' guard.
42
+
43
+ Returns:
44
+ True if the file has required structure.
45
+ """
46
+ return super().is_correct() or (
47
+ "def main" in cls.get_file_content()
48
+ and 'if __name__ == "__main__":' in cls.get_file_content()
49
+ )
50
+
51
+ @classmethod
52
+ def delete_root_main(cls) -> None:
53
+ """Delete any root-level main.py file.
54
+
55
+ Cleans up legacy main.py files that should be in src/.
56
+ """
57
+ root_main_path = Path("main.py")
58
+ if root_main_path.exists():
59
+ root_main_path.unlink()
@@ -0,0 +1,27 @@
1
+ """Configuration for the resources package __init__.py.
2
+
3
+ This module provides the ResourcesInitConfigFile class for creating
4
+ the dev/artifacts/resources directory structure with an __init__.py file.
5
+ """
6
+
7
+ from types import ModuleType
8
+
9
+ from pyrig import resources
10
+ from pyrig.dev.configs.base.base import InitConfigFile
11
+
12
+
13
+ class ResourcesInitConfigFile(InitConfigFile):
14
+ """Configuration file manager for resources/__init__.py.
15
+
16
+ Creates the dev/artifacts/resources directory with an __init__.py
17
+ file that mirrors pyrig's resources package structure.
18
+ """
19
+
20
+ @classmethod
21
+ def get_src_module(cls) -> ModuleType:
22
+ """Get the source module to mirror.
23
+
24
+ Returns:
25
+ The pyrig.dev.artifacts.resources module.
26
+ """
27
+ return resources
@@ -0,0 +1,29 @@
1
+ """Shared subcommands ConfigFile.
2
+
3
+ This module provides the SharedSubcommandsConfigFile class for creating
4
+ a shared_subcommands.py file where users can define custom CLI subcommands
5
+ that are available in all pyrig projects.
6
+ """
7
+
8
+ from types import ModuleType
9
+
10
+ from pyrig.dev.cli import shared_subcommands
11
+ from pyrig.dev.configs.base.base import CopyModuleOnlyDocstringConfigFile
12
+
13
+
14
+ class SharedSubcommandsConfigFile(CopyModuleOnlyDocstringConfigFile):
15
+ """Configuration file manager for shared_subcommands.py.
16
+
17
+ Creates a shared_subcommands.py file with only the docstring from pyrig's
18
+ shared_subcommands module, allowing users to add custom CLI subcommands
19
+ that are available in all pyrig projects.
20
+ """
21
+
22
+ @classmethod
23
+ def get_src_module(cls) -> ModuleType:
24
+ """Get the source module to copy docstring from.
25
+
26
+ Returns:
27
+ The pyrig.dev.cli.shared_subcommands module.
28
+ """
29
+ return shared_subcommands
@@ -0,0 +1,27 @@
1
+ """Configuration for the src package __init__.py.
2
+
3
+ This module provides the SrcInitConfigFile class for creating
4
+ the src directory structure with an __init__.py file.
5
+ """
6
+
7
+ from types import ModuleType
8
+
9
+ from pyrig import src
10
+ from pyrig.dev.configs.base.base import InitConfigFile
11
+
12
+
13
+ class SrcInitConfigFile(InitConfigFile):
14
+ """Configuration file manager for src/__init__.py.
15
+
16
+ Creates the src directory with an __init__.py file that
17
+ mirrors pyrig's src package structure.
18
+ """
19
+
20
+ @classmethod
21
+ def get_src_module(cls) -> ModuleType:
22
+ """Get the source module to mirror.
23
+
24
+ Returns:
25
+ The pyrig.src module.
26
+ """
27
+ return src