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
pyrig/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """__init__ module."""
pyrig/dev/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Development-time infrastructure for pyrig projects.
2
+
3
+ This package contains development tools, configuration management, CLI,
4
+ artifact building, and testing infrastructure. These components are used
5
+ during development and CI/CD but are not required at runtime.
6
+ """
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1,5 @@
1
+ """Base classes for artifact builders.
2
+
3
+ This package provides the abstract Builder base class that all
4
+ artifact builders must inherit from.
5
+ """
@@ -0,0 +1,256 @@
1
+ """Abstract base classes for artifact builders.
2
+
3
+ This module provides the ``Builder`` and ``PyInstallerBuilder`` abstract
4
+ base classes for creating distributable artifacts. Subclass these to
5
+ define custom build processes for your project.
6
+
7
+ The builder system uses automatic discovery: all non-abstract Builder
8
+ subclasses across packages depending on pyrig are found and invoked
9
+ when running ``pyrig build``.
10
+
11
+ Example:
12
+ Create a custom builder by subclassing PyInstallerBuilder:
13
+
14
+ class MyAppBuilder(PyInstallerBuilder):
15
+ @classmethod
16
+ def get_additional_resource_pkgs(cls) -> list[ModuleType]:
17
+ return [my_resources_package]
18
+
19
+ Attributes:
20
+ ARTIFACTS_DIR_NAME: Default output directory for artifacts ("dist").
21
+ """
22
+
23
+ import platform
24
+ import shutil
25
+ import tempfile
26
+ from abc import ABC, abstractmethod
27
+ from pathlib import Path
28
+
29
+ import pyrig
30
+ from pyrig import main, resources
31
+ from pyrig.dev import builders
32
+ from pyrig.dev.configs.pyproject import PyprojectConfigFile
33
+ from pyrig.dev.utils.packages import get_src_package
34
+ from pyrig.src.modules.class_ import (
35
+ get_all_nonabst_subcls_from_mod_in_all_deps_depen_on_dep,
36
+ )
37
+ from pyrig.src.modules.module import (
38
+ to_path,
39
+ )
40
+
41
+
42
+ class Builder(ABC):
43
+ """Abstract base class for artifact builders.
44
+
45
+ Subclass this class and implement ``create_artifacts`` to define
46
+ a custom build process. The build is triggered automatically when
47
+ the class is instantiated.
48
+
49
+ Subclasses must implement:
50
+ - ``create_artifacts``: Create artifacts in the provided temp directory
51
+
52
+ Attributes:
53
+ ARTIFACTS_DIR_NAME: Output directory name for built artifacts.
54
+
55
+ Example:
56
+ class MyBuilder(Builder):
57
+ @classmethod
58
+ def create_artifacts(cls, temp_artifacts_dir: Path) -> None:
59
+ # Create your artifacts here
60
+ pass
61
+
62
+ if __name__ == "__main__":
63
+ MyBuilder()
64
+ """
65
+
66
+ ARTIFACTS_DIR_NAME = "dist"
67
+
68
+ @classmethod
69
+ @abstractmethod
70
+ def create_artifacts(cls, temp_artifacts_dir: Path) -> None:
71
+ """Create artifacts in the temporary directory.
72
+
73
+ Subclasses must implement this method to define the build process.
74
+ All artifacts should be written to ``temp_artifacts_dir``.
75
+
76
+ Args:
77
+ temp_artifacts_dir: Temporary directory where artifacts should
78
+ be created. Contents will be moved to the final output
79
+ directory after the build completes.
80
+ """
81
+
82
+ def __init__(self) -> None:
83
+ """Initialize the builder and trigger the build process."""
84
+ self.__class__.build()
85
+
86
+ @classmethod
87
+ def get_artifacts_dir(cls) -> Path:
88
+ """Get the final output directory for artifacts.
89
+
90
+ Returns:
91
+ Path to the artifacts directory (default: "dist").
92
+ """
93
+ return Path(cls.ARTIFACTS_DIR_NAME)
94
+
95
+ @classmethod
96
+ def build(cls) -> None:
97
+ """Execute the build process.
98
+
99
+ Creates a temporary directory, invokes ``create_artifacts``,
100
+ then moves and renames artifacts to the final output directory
101
+ with platform-specific suffixes.
102
+ """
103
+ with tempfile.TemporaryDirectory() as temp_build_dir:
104
+ temp_dir_path = Path(temp_build_dir)
105
+ temp_artifacts_dir = cls.get_temp_artifacts_path(temp_dir_path)
106
+ cls.create_artifacts(temp_artifacts_dir)
107
+ artifacts = cls.get_temp_artifacts(temp_artifacts_dir)
108
+ cls.rename_artifacts(artifacts)
109
+
110
+ @classmethod
111
+ def rename_artifacts(cls, artifacts: list[Path]) -> None:
112
+ """Move artifacts to output directory with platform-specific names.
113
+
114
+ Args:
115
+ artifacts: List of artifact paths to rename and move.
116
+ """
117
+ artifacts_dir = cls.get_artifacts_dir()
118
+ artifacts_dir.mkdir(parents=True, exist_ok=True)
119
+ for artifact in artifacts:
120
+ # rename the files with -platform.system()
121
+ new_name = f"{artifact.stem}-{platform.system()}{artifact.suffix}"
122
+ new_path = artifacts_dir / new_name
123
+ shutil.move(str(artifact), str(new_path))
124
+
125
+ @classmethod
126
+ def get_temp_artifacts(cls, temp_artifacts_dir: Path) -> list[Path]:
127
+ """Get all artifacts from the temporary build directory.
128
+
129
+ Args:
130
+ temp_artifacts_dir: Path to the temporary artifacts directory.
131
+
132
+ Returns:
133
+ List of paths to built artifacts.
134
+
135
+ Raises:
136
+ FileNotFoundError: If no artifacts were created.
137
+ """
138
+ paths = list(temp_artifacts_dir.glob("*"))
139
+ if not paths:
140
+ msg = f"Expected {temp_artifacts_dir} to contain files"
141
+ raise FileNotFoundError(msg)
142
+ return paths
143
+
144
+ @classmethod
145
+ def get_artifacts(cls) -> list[Path]:
146
+ """Get all artifacts from the final output directory.
147
+
148
+ Returns:
149
+ List of paths to built artifacts in the output directory.
150
+ """
151
+ return list(cls.get_artifacts_dir().glob("*"))
152
+
153
+ @classmethod
154
+ def get_temp_artifacts_path(cls, temp_dir: Path) -> Path:
155
+ """Create and return the temporary artifacts subdirectory.
156
+
157
+ Args:
158
+ temp_dir: Parent temporary directory.
159
+
160
+ Returns:
161
+ Path to the created artifacts subdirectory.
162
+ """
163
+ path = temp_dir / cls.ARTIFACTS_DIR_NAME
164
+ path.mkdir(parents=True, exist_ok=True)
165
+ return path
166
+
167
+ @classmethod
168
+ def get_non_abstract_subclasses(cls) -> list[type["Builder"]]:
169
+ """Discover all non-abstract Builder subclasses.
170
+
171
+ Searches all packages depending on pyrig for Builder subclasses.
172
+ Parent classes are discarded so only leaf implementations are returned.
173
+
174
+ Returns:
175
+ List of non-abstract Builder subclass types.
176
+ """
177
+ return get_all_nonabst_subcls_from_mod_in_all_deps_depen_on_dep(
178
+ cls,
179
+ pyrig,
180
+ builders,
181
+ discard_parents=True,
182
+ )
183
+
184
+ @classmethod
185
+ def init_all_non_abstract_subclasses(cls) -> None:
186
+ """Instantiate all discovered Builder subclasses to trigger builds."""
187
+ for builder_cls in cls.get_non_abstract_subclasses():
188
+ builder_cls()
189
+
190
+ @classmethod
191
+ def get_app_name(cls) -> str:
192
+ """Get the application name from pyproject.toml.
193
+
194
+ Returns:
195
+ The project name as defined in pyproject.toml.
196
+ """
197
+ return PyprojectConfigFile.get_project_name()
198
+
199
+ @classmethod
200
+ def get_root_path(cls) -> Path:
201
+ """Get the project root directory path.
202
+
203
+ Returns:
204
+ Path to the project root (parent of the source package).
205
+ """
206
+ src_pkg = get_src_package()
207
+ return to_path(src_pkg, is_package=True).resolve().parent
208
+
209
+ @classmethod
210
+ def get_main_path(cls) -> Path:
211
+ """Get the absolute path to the main.py entry point.
212
+
213
+ Returns:
214
+ Path to the main.py file in the source package.
215
+ """
216
+ return cls.get_src_pkg_path() / cls.get_main_path_from_src_pkg()
217
+
218
+ @classmethod
219
+ def get_resources_path(cls) -> Path:
220
+ """Get the absolute path to the resources directory.
221
+
222
+ Returns:
223
+ Path to the dev/artifacts/resources directory.
224
+ """
225
+ return cls.get_src_pkg_path() / cls.get_resources_path_from_src_pkg()
226
+
227
+ @classmethod
228
+ def get_src_pkg_path(cls) -> Path:
229
+ """Get the absolute path to the source package.
230
+
231
+ Returns:
232
+ Path to the source package directory.
233
+ """
234
+ return cls.get_root_path() / PyprojectConfigFile.get_package_name()
235
+
236
+ @classmethod
237
+ def get_main_path_from_src_pkg(cls) -> Path:
238
+ """Get the relative path to main.py from the source package.
239
+
240
+ Returns:
241
+ Relative path from source package to main.py.
242
+ """
243
+ return to_path(main, is_package=False).relative_to(
244
+ to_path(pyrig, is_package=True)
245
+ )
246
+
247
+ @classmethod
248
+ def get_resources_path_from_src_pkg(cls) -> Path:
249
+ """Get the relative path to resources from the source package.
250
+
251
+ Returns:
252
+ Relative path from source package to resources directory.
253
+ """
254
+ return to_path(resources, is_package=True).relative_to(
255
+ to_path(pyrig, is_package=True)
256
+ )
@@ -0,0 +1,229 @@
1
+ """PyInstaller-based artifact builder.
2
+
3
+ This module provides the PyInstallerBuilder class for creating
4
+ standalone executables from pyrig projects using PyInstaller.
5
+ """
6
+
7
+ import os
8
+ import platform
9
+ from abc import abstractmethod
10
+ from pathlib import Path
11
+ from types import ModuleType
12
+
13
+ from PIL import Image
14
+ from PyInstaller.__main__ import run
15
+ from PyInstaller.utils.hooks import collect_data_files
16
+
17
+ import pyrig
18
+ from pyrig import resources
19
+ from pyrig.dev.builders.base.base import Builder
20
+ from pyrig.src.modules.module import get_same_modules_from_deps_depen_on_dep
21
+
22
+
23
+ class PyInstallerBuilder(Builder):
24
+ """Abstract builder for creating PyInstaller executables.
25
+
26
+ Subclass this to create standalone executables from your project.
27
+ The builder handles icon conversion, resource bundling, and
28
+ platform-specific configuration.
29
+
30
+ Subclasses must implement:
31
+ - ``get_additional_resource_pkgs``: Return packages containing resources
32
+
33
+ The builder automatically includes:
34
+ - All resources from dev/artifacts/resources directories
35
+ - Resources from all packages depending on pyrig
36
+ - Platform-appropriate icon format (ico/icns/png)
37
+
38
+ Example:
39
+ class MyAppBuilder(PyInstallerBuilder):
40
+ @classmethod
41
+ def get_additional_resource_pkgs(cls) -> list[ModuleType]:
42
+ return [my_app.resources]
43
+ """
44
+
45
+ @classmethod
46
+ def create_artifacts(cls, temp_artifacts_dir: Path) -> None:
47
+ """Build a PyInstaller executable.
48
+
49
+ Args:
50
+ temp_artifacts_dir: Directory where the executable will be created.
51
+ """
52
+ options = cls.get_pyinstaller_options(temp_artifacts_dir)
53
+ run(options)
54
+
55
+ @classmethod
56
+ @abstractmethod
57
+ def get_additional_resource_pkgs(cls) -> list[ModuleType]:
58
+ """Return packages containing additional resources to bundle.
59
+
60
+ Override this method to specify packages whose contents should
61
+ be included in the executable. All files in these packages will
62
+ be bundled and accessible at runtime.
63
+
64
+ Returns:
65
+ List of module objects representing resource packages.
66
+
67
+ Note:
68
+ The dev/artifacts/resources package and resources from all
69
+ pyrig-dependent packages are included automatically.
70
+ """
71
+
72
+ @classmethod
73
+ def get_default_additional_resource_pkgs(cls) -> list[ModuleType]:
74
+ """Get resource packages from all pyrig-dependent packages.
75
+
76
+ Returns:
77
+ List of resources modules from packages depending on pyrig.
78
+ """
79
+ return get_same_modules_from_deps_depen_on_dep(resources, pyrig)
80
+
81
+ @classmethod
82
+ def get_all_resource_pkgs(cls) -> list[ModuleType]:
83
+ """Get all resource packages to bundle.
84
+
85
+ Returns:
86
+ Combined list of default and additional resource packages.
87
+ """
88
+ return [
89
+ *cls.get_default_additional_resource_pkgs(),
90
+ *cls.get_additional_resource_pkgs(),
91
+ ]
92
+
93
+ @classmethod
94
+ def get_add_datas(cls) -> list[tuple[str, str]]:
95
+ """Build the --add-data arguments for PyInstaller.
96
+
97
+ Uses PyInstaller's collect_data_files to automatically gather
98
+ all data files from resource packages.
99
+
100
+ Returns:
101
+ List of (source_path, destination_path) tuples.
102
+ """
103
+ add_datas: list[tuple[str, str]] = []
104
+ resources_pkgs = cls.get_all_resource_pkgs()
105
+ for pkg in resources_pkgs:
106
+ # collect_data_files returns list of (source, dest) tuples
107
+ pkg_datas = collect_data_files(pkg.__name__, include_py_files=True)
108
+ add_datas.extend(pkg_datas)
109
+ return add_datas
110
+
111
+ @classmethod
112
+ def get_pyinstaller_options(cls, temp_artifacts_dir: Path) -> list[str]:
113
+ """Build the complete PyInstaller command-line options.
114
+
115
+ Args:
116
+ temp_artifacts_dir: Directory for build output.
117
+
118
+ Returns:
119
+ List of command-line arguments for PyInstaller.
120
+ """
121
+ temp_dir = temp_artifacts_dir.parent
122
+
123
+ options = [
124
+ str(cls.get_main_path()),
125
+ "--name",
126
+ cls.get_app_name(),
127
+ "--clean",
128
+ "--noconfirm",
129
+ "--onefile",
130
+ "--noconsole",
131
+ "--workpath",
132
+ str(cls.get_temp_workpath(temp_dir)),
133
+ "--specpath",
134
+ str(cls.get_temp_specpath(temp_dir)),
135
+ "--distpath",
136
+ str(cls.get_temp_distpath(temp_dir)),
137
+ "--icon",
138
+ str(cls.get_app_icon_path(temp_dir)),
139
+ ]
140
+ for src, dest in cls.get_add_datas():
141
+ options.extend(["--add-data", f"{src}{os.pathsep}{dest}"])
142
+ return options
143
+
144
+ @classmethod
145
+ def get_temp_distpath(cls, temp_dir: Path) -> Path:
146
+ """Get the temporary distribution output path.
147
+
148
+ Args:
149
+ temp_dir: Parent temporary directory.
150
+
151
+ Returns:
152
+ Path to the dist subdirectory.
153
+ """
154
+ return cls.get_temp_artifacts_path(temp_dir)
155
+
156
+ @classmethod
157
+ def get_temp_workpath(cls, temp_dir: Path) -> Path:
158
+ """Get the temporary work directory for PyInstaller.
159
+
160
+ Args:
161
+ temp_dir: Parent temporary directory.
162
+
163
+ Returns:
164
+ Path to the workpath subdirectory.
165
+ """
166
+ path = temp_dir / "workpath"
167
+ path.mkdir(parents=True, exist_ok=True)
168
+ return path
169
+
170
+ @classmethod
171
+ def get_temp_specpath(cls, temp_dir: Path) -> Path:
172
+ """Get the temporary spec file directory.
173
+
174
+ Args:
175
+ temp_dir: Parent temporary directory.
176
+
177
+ Returns:
178
+ Path to the specpath subdirectory.
179
+ """
180
+ path = temp_dir / "specpath"
181
+ path.mkdir(parents=True, exist_ok=True)
182
+ return path
183
+
184
+ @classmethod
185
+ def get_app_icon_path(cls, temp_dir: Path) -> Path:
186
+ """Get the platform-appropriate icon path.
187
+
188
+ Converts the PNG icon to the appropriate format for the
189
+ current platform (ico for Windows, icns for macOS).
190
+
191
+ Args:
192
+ temp_dir: Directory for the converted icon file.
193
+
194
+ Returns:
195
+ Path to the converted icon file.
196
+ """
197
+ if platform.system() == "Windows":
198
+ return cls.convert_png_to_format("ico", temp_dir)
199
+ if platform.system() == "Darwin":
200
+ return cls.convert_png_to_format("icns", temp_dir)
201
+ return cls.convert_png_to_format("png", temp_dir)
202
+
203
+ @classmethod
204
+ def convert_png_to_format(cls, file_format: str, temp_dir_path: Path) -> Path:
205
+ """Convert the application icon PNG to another format.
206
+
207
+ Args:
208
+ file_format: Target format (ico, icns, or png).
209
+ temp_dir_path: Directory for the output file.
210
+
211
+ Returns:
212
+ Path to the converted icon file.
213
+ """
214
+ output_path = temp_dir_path / f"icon.{file_format}"
215
+ png_path = cls.get_app_icon_png_path()
216
+ img = Image.open(png_path)
217
+ img.save(output_path, format=file_format.upper())
218
+ return output_path
219
+
220
+ @classmethod
221
+ def get_app_icon_png_path(cls) -> Path:
222
+ """Get the path to the application icon PNG.
223
+
224
+ Override this method to use a custom icon location.
225
+
226
+ Returns:
227
+ Path to icon.png in the resources directory.
228
+ """
229
+ return cls.get_resources_path() / "icon.png"
@@ -0,0 +1,5 @@
1
+ """Command-line interface for pyrig.
2
+
3
+ This package provides the CLI entry point and subcommands for pyrig.
4
+ Commands are automatically discovered from the subcommands module.
5
+ """
pyrig/dev/cli/cli.py ADDED
@@ -0,0 +1,95 @@
1
+ """CLI entry point and command registration.
2
+
3
+ This module provides the main CLI entry point for pyrig projects. It uses
4
+ Typer to build a command-line interface and automatically discovers
5
+ subcommands from the project's subcommands module.
6
+
7
+ The CLI supports both pyrig's built-in commands and project-specific
8
+ commands defined in the consuming project's subcommands module.
9
+
10
+ Example:
11
+ $ uv run pyrig init
12
+ $ uv run pyrig create-root
13
+ $ uv run pyrig build
14
+ """
15
+
16
+ from importlib import import_module
17
+
18
+ import typer
19
+
20
+ import pyrig
21
+ from pyrig import main as pyrig_main
22
+ from pyrig.dev.cli import shared_subcommands, subcommands
23
+ from pyrig.dev.utils.cli import get_pkg_name_from_argv
24
+ from pyrig.src.modules.function import get_all_functions_from_module
25
+ from pyrig.src.modules.module import (
26
+ get_module_name_replacing_start_module,
27
+ get_same_modules_from_deps_depen_on_dep,
28
+ import_module_from_file,
29
+ )
30
+
31
+ app = typer.Typer(no_args_is_help=True)
32
+ """The main Typer application instance."""
33
+
34
+
35
+ def add_subcommands() -> None:
36
+ """Discover and register all CLI subcommands.
37
+
38
+ Dynamically loads the main module and subcommands module for the
39
+ current project, registering all public functions as CLI commands.
40
+ This enables projects depending on pyrig to define their own commands.
41
+ """
42
+ # extract project name from sys.argv[0]
43
+ pkg_name = get_pkg_name_from_argv()
44
+
45
+ main_module_name = get_module_name_replacing_start_module(pyrig_main, pkg_name)
46
+ main_module = import_module_from_file(main_module_name)
47
+ app.command()(main_module.main)
48
+
49
+ # replace the first parent with pkg_name
50
+ subcommands_module_name = get_module_name_replacing_start_module(
51
+ subcommands, pkg_name
52
+ )
53
+
54
+ subcommands_module = import_module_from_file(subcommands_module_name)
55
+
56
+ sub_cmds = get_all_functions_from_module(subcommands_module)
57
+
58
+ for sub_cmd in sub_cmds:
59
+ app.command()(sub_cmd)
60
+
61
+
62
+ def add_shared_subcommands() -> None:
63
+ """Discover and register all shared CLI subcommands.
64
+
65
+ This discovers all packages inheriting from pyrig and loads their
66
+ shared_subcommands modules, registering all public functions as CLI
67
+ commands. This enables cross-package commands that are available
68
+ in all pyrig projects. Example is pyrigs version command that is
69
+ available in all pyrig projects.
70
+ So you can do:
71
+ uv run pyrig version -> pyrig version 0.1.0
72
+ uv run my-awesome-project version -> my-awesome-project version 0.1.0
73
+ """
74
+ package_name = get_pkg_name_from_argv()
75
+ package = import_module(package_name)
76
+ all_shared_subcommands_modules = get_same_modules_from_deps_depen_on_dep(
77
+ shared_subcommands,
78
+ pyrig,
79
+ until_pkg=package,
80
+ )
81
+ for shared_subcommands_module in all_shared_subcommands_modules:
82
+ sub_cmds = get_all_functions_from_module(shared_subcommands_module)
83
+ for sub_cmd in sub_cmds:
84
+ app.command()(sub_cmd)
85
+
86
+
87
+ def main() -> None:
88
+ """Entry point for the CLI.
89
+
90
+ Registers all subcommands and invokes the Typer application.
91
+ This function is called by the console script entry point.
92
+ """
93
+ add_subcommands()
94
+ add_shared_subcommands()
95
+ app()
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1,16 @@
1
+ """Artifact build orchestration.
2
+
3
+ This module provides the main entry point for building all project
4
+ artifacts. It discovers all Builder subclasses and invokes each one.
5
+ """
6
+
7
+ from pyrig.dev.builders.base.base import Builder
8
+
9
+
10
+ def build_artifacts() -> None:
11
+ """Build all artifacts by invoking all registered Builder subclasses.
12
+
13
+ Discovers all non-abstract Builder subclasses across all packages
14
+ depending on pyrig and invokes each one to create its artifacts.
15
+ """
16
+ Builder.init_all_non_abstract_subclasses()
@@ -0,0 +1,25 @@
1
+ """Project structure creation utilities.
2
+
3
+ This module provides the `create_root` function which generates all
4
+ configuration files and directory structure for a pyrig project.
5
+ It delegates to the ConfigFile system to discover and initialize
6
+ all registered config file types.
7
+ """
8
+
9
+ from pyrig.dev.configs.base.base import ConfigFile
10
+
11
+
12
+ def make_project_root() -> None:
13
+ """Create all configuration files and project structure.
14
+
15
+ Discovers all ConfigFile subclasses and initializes each one,
16
+ creating the complete project structure including:
17
+ - pyproject.toml
18
+ - GitHub workflows
19
+ - Pre-commit configuration
20
+ - Ruff/mypy configuration
21
+ - Source and test directory structure
22
+
23
+ This is the implementation for the `pyrig create-root` command.
24
+ """
25
+ ConfigFile.init_config_files()