forgepy-cli 1.0.0__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.
- builders/__init__.py +0 -0
- builders/base_builder.py +17 -0
- builders/file_builder.py +31 -0
- builders/folder_builder.py +30 -0
- builders/python_tools_builder.py +64 -0
- cli/__init__.py +3 -0
- cli/command.py +52 -0
- cli/commands/__init__.py +32 -0
- cli/commands/component_command.py +177 -0
- cli/commands/config_command.py +174 -0
- cli/commands/create_command.py +161 -0
- cli/commands/list_command.py +40 -0
- cli/commands/version_command.py +29 -0
- cli/dispatcher.py +61 -0
- cli/parser.py +84 -0
- components/__init__.py +1 -0
- components/base_component.py +30 -0
- components/component_context.py +21 -0
- components/component_installer.py +44 -0
- components/component_manifest.py +102 -0
- components/component_metadata.py +50 -0
- components/component_registry.py +74 -0
- components/component_state.py +243 -0
- components/component_validation.py +88 -0
- components/github_actions_component.py +75 -0
- components/pytest_component.py +40 -0
- components/ruff_component.py +40 -0
- config/__init__.py +0 -0
- config/default_structure.py +18 -0
- config/user_config.py +238 -0
- config/version.py +16 -0
- core/__init__.py +0 -0
- core/environment_builder.py +33 -0
- core/git_builder.py +90 -0
- core/project_generator.py +125 -0
- core/requirements_installer.py +50 -0
- core/vscode_builder.py +103 -0
- forgepy_cli-1.0.0.dist-info/METADATA +176 -0
- forgepy_cli-1.0.0.dist-info/RECORD +74 -0
- forgepy_cli-1.0.0.dist-info/WHEEL +5 -0
- forgepy_cli-1.0.0.dist-info/entry_points.txt +2 -0
- forgepy_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
- forgepy_cli-1.0.0.dist-info/top_level.txt +8 -0
- main.py +20 -0
- models/__init__.py +1 -0
- models/project_config.py +87 -0
- templates/__init__.py +0 -0
- templates/app_template.py +20 -0
- templates/basic/basic_files.py +23 -0
- templates/basic/basic_template.py +32 -0
- templates/changelog_template.py +15 -0
- templates/cli/cli_files.py +70 -0
- templates/cli/cli_template.py +55 -0
- templates/env_template.py +20 -0
- templates/gitignore_template.py +27 -0
- templates/library/library_files.py +28 -0
- templates/library/library_template.py +56 -0
- templates/license_template.py +21 -0
- templates/pyproject_template.py +15 -0
- templates/readme_template.py +15 -0
- templates/requirements_template.py +13 -0
- templates/template_engine/base_template.py +58 -0
- templates/template_engine/file_template.py +76 -0
- templates/template_engine/package_name.py +30 -0
- templates/template_engine/template_context.py +24 -0
- templates/template_engine/template_files.py +10 -0
- templates/template_engine/template_metadata.py +59 -0
- templates/template_engine/template_registry.py +107 -0
- templates/template_manager.py +58 -0
- templates/vscode/__init__.py +0 -0
- templates/vscode/extensions_template.py +32 -0
- templates/vscode/launch_template.py +41 -0
- templates/vscode/settings_template.py +37 -0
- templates/vscode/tasks_template.py +66 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: forgepy-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Create structured Python projects and prepare their development tooling.
|
|
5
|
+
Author: Rendy Zou
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/rzou89/ForgePy
|
|
8
|
+
Project-URL: Repository, https://github.com/rzou89/ForgePy
|
|
9
|
+
Project-URL: Issues, https://github.com/rzou89/ForgePy/issues
|
|
10
|
+
Keywords: python,project-generator,cli,windows
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# ForgePy
|
|
25
|
+
|
|
26
|
+
ForgePy is a Windows-focused command-line tool that creates structured Python projects and prepares their development tooling.
|
|
27
|
+
|
|
28
|
+
## What ForgePy Does
|
|
29
|
+
|
|
30
|
+
ForgePy generates a project from one of its built-in templates, creates a virtual environment, installs the generated requirements, writes VS Code configuration, and initializes the project as a Git repository with an initial commit. It also provides optional components that can add focused configuration files to an existing project.
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- Windows 10 or Windows 11
|
|
35
|
+
- CPython 3.12 or newer
|
|
36
|
+
- Git, required for project creation and configured with the user name and email needed to create a commit
|
|
37
|
+
|
|
38
|
+
Other operating systems and alternative Python implementations are not officially supported for ForgePy v1.0.
|
|
39
|
+
|
|
40
|
+
Repository CI tests ForgePy on GitHub-hosted `windows-latest` runners using CPython 3.12, 3.13, and 3.14. This verifies compatibility with the hosted Windows environment; it does not literally test native Windows 10 and Windows 11 client installations.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
ForgePy is prepared for PyPI publication under the distribution name
|
|
45
|
+
`forgepy-cli`. After that first publication, install it with:
|
|
46
|
+
|
|
47
|
+
```powershell
|
|
48
|
+
python -m pip install forgepy-cli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The PyPI upload is still pending. Until it is completed, install from source as
|
|
52
|
+
described below or use the existing GitHub v1.0.0 release artifacts.
|
|
53
|
+
|
|
54
|
+
From a ForgePy source checkout, install the command with:
|
|
55
|
+
|
|
56
|
+
```powershell
|
|
57
|
+
python -m pip install .
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For editable development work, use:
|
|
61
|
+
|
|
62
|
+
```powershell
|
|
63
|
+
python -m pip install -e .
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
Check the installed versions and available templates:
|
|
69
|
+
|
|
70
|
+
```powershell
|
|
71
|
+
forgepy version
|
|
72
|
+
forgepy list
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Create a basic project below an existing parent directory:
|
|
76
|
+
|
|
77
|
+
```powershell
|
|
78
|
+
forgepy create MyProject --location C:\Projects --template basic
|
|
79
|
+
cd C:\Projects\MyProject
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The destination must not already exist. Omitting the project name or location starts the corresponding prompt; an omitted template uses the configured default and then falls back to `basic`.
|
|
83
|
+
|
|
84
|
+
## Available Templates
|
|
85
|
+
|
|
86
|
+
- `basic` - a basic application starter with a structured directory layout, `app.py`, project metadata, environment files, and generated requirements.
|
|
87
|
+
- `library` - a minimal reusable Python package with a normalized import-package directory and a `tests` package.
|
|
88
|
+
- `cli` - a minimal command-line package with `__main__.py`, an argparse interface, help and version behavior, and a `tests` package.
|
|
89
|
+
|
|
90
|
+
List the registered templates at any time with `forgepy list`.
|
|
91
|
+
|
|
92
|
+
## Components
|
|
93
|
+
|
|
94
|
+
Components add one focused configuration file to an explicitly supplied existing project directory. ForgePy records successful component installations in project-local state.
|
|
95
|
+
|
|
96
|
+
Available components are:
|
|
97
|
+
|
|
98
|
+
- `pytest` - adds `pytest.ini`.
|
|
99
|
+
- `ruff` - adds `ruff.toml`.
|
|
100
|
+
- `github-actions` - adds a minimal `.github/workflows/ci.yml` for the generated project.
|
|
101
|
+
|
|
102
|
+
Use the verified component commands:
|
|
103
|
+
|
|
104
|
+
```powershell
|
|
105
|
+
forgepy component list
|
|
106
|
+
forgepy component add pytest --project C:\Projects\MyProject
|
|
107
|
+
forgepy component installed --project C:\Projects\MyProject
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Component installation refuses an owned target that already exists. The generated-project `github-actions` component is separate from ForgePy's own repository CI workflow.
|
|
111
|
+
|
|
112
|
+
## Configuration
|
|
113
|
+
|
|
114
|
+
ForgePy stores user configuration under `~/.forgepy/config.json`. Supported settings are `default_template`, `default_location`, `author`, and `license`.
|
|
115
|
+
|
|
116
|
+
```powershell
|
|
117
|
+
forgepy config show
|
|
118
|
+
forgepy config set default_template library
|
|
119
|
+
forgepy config set default_location C:\Projects
|
|
120
|
+
forgepy config reset
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Only `default_template` and `default_location` currently affect project creation. `author` and `license` are persisted but are not applied to generated files.
|
|
124
|
+
|
|
125
|
+
Explicit `create` options take priority over configuration. See all supported syntax with:
|
|
126
|
+
|
|
127
|
+
```powershell
|
|
128
|
+
forgepy create --help
|
|
129
|
+
forgepy config --help
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Project Creation Lifecycle
|
|
133
|
+
|
|
134
|
+
A successful `forgepy create` runs these stages in order:
|
|
135
|
+
|
|
136
|
+
1. Generate the selected template.
|
|
137
|
+
2. Create `.venv`.
|
|
138
|
+
3. Update `pip`, `setuptools`, and `wheel` in that environment.
|
|
139
|
+
4. Install the generated `requirements.txt`.
|
|
140
|
+
5. Write VS Code configuration.
|
|
141
|
+
6. Run `git init`.
|
|
142
|
+
7. Run `git add .`.
|
|
143
|
+
8. Create the initial Git commit.
|
|
144
|
+
9. Report full success.
|
|
145
|
+
|
|
146
|
+
Git is a required part of successful project creation. Packaging-tool updates and dependency installation may require network access.
|
|
147
|
+
|
|
148
|
+
## Failure and Partial Projects
|
|
149
|
+
|
|
150
|
+
If creation fails after ForgePy creates the destination, later stages stop and full success is not reported. Files generated by earlier stages may remain in the destination because ForgePy does not automatically roll back or clean up a partial project.
|
|
151
|
+
|
|
152
|
+
Inspect the destination and remove it when appropriate before retrying. ForgePy project creation is not transactional or atomic.
|
|
153
|
+
|
|
154
|
+
## Development
|
|
155
|
+
|
|
156
|
+
Run the standard local validation from the repository root:
|
|
157
|
+
|
|
158
|
+
```powershell
|
|
159
|
+
python -m unittest discover -s tests -v
|
|
160
|
+
python -m compileall -q components cli templates core config builders models tests
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Repository CI runs the test suite, compilation check, and packaging/support tests on `windows-latest` with CPython 3.12, 3.13, and 3.14. Its Python 3.12 job also builds and inspects the wheel and sdist, installs the wheel in isolation, and exercises the installed CLI.
|
|
164
|
+
|
|
165
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for repository workflow and review expectations.
|
|
166
|
+
|
|
167
|
+
## Current Status
|
|
168
|
+
|
|
169
|
+
ForgePy v1.0.0 is the current stable Git tag and GitHub Release. The application
|
|
170
|
+
and command remain named ForgePy and `forgepy`; only the prepared PyPI
|
|
171
|
+
distribution name is `forgepy-cli`. Trusted Publishing is configured in the
|
|
172
|
+
repository, but the first PyPI upload has not occurred.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
ForgePy is licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
main.py,sha256=b6pnUHEuAWbxnY6Nv_y27dMdmXW0yG9uhMAKREjXqZE,388
|
|
2
|
+
builders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
builders/base_builder.py,sha256=93Y57jc2g6HrWmSIQGsr_800_vXj9BJlYda9N9Esln0,320
|
|
4
|
+
builders/file_builder.py,sha256=sWZ_3bUSC5cGdfngwsmOAVcDbnEcwP_kOqqgmJ958x0,558
|
|
5
|
+
builders/folder_builder.py,sha256=_BsDubymSD-ddFFWJyxonzNe1iGRERE09HqqrSzHgeg,563
|
|
6
|
+
builders/python_tools_builder.py,sha256=ITRoAdnyZBaViqclpWENyzLZEw6lizML9592xCs3_5c,1557
|
|
7
|
+
cli/__init__.py,sha256=GyGkgk8eLqUOaW69TRbW50sRNjC-wgJWls4jcTn-LXI,27
|
|
8
|
+
cli/command.py,sha256=uuWh3-sswzqODlk17PI0M19vh5I-QA3E2rq1UwEeL8g,1230
|
|
9
|
+
cli/dispatcher.py,sha256=WfXMcmrOSd4dmUVF1HTKtR71Tcj6GUA7GDQUK3IHV-I,1603
|
|
10
|
+
cli/parser.py,sha256=CnheRTyPcSsrUqUbxSwMsczsmTQwiy13_uRx6kp01o0,2078
|
|
11
|
+
cli/commands/__init__.py,sha256=DluqEyUZvcjBN5yFXQ5um5qMCyce4hrPMC0FaGF5UJY,811
|
|
12
|
+
cli/commands/component_command.py,sha256=EceEwm7a35kv6ArftQj0KybIhtWOzGbhbLWWXz1TK_Q,5699
|
|
13
|
+
cli/commands/config_command.py,sha256=q9tKqf_RVW60UBWMWEPCRzjJZUIf58NjQlFUzZQJgBg,4596
|
|
14
|
+
cli/commands/create_command.py,sha256=xJhEWL-syTzFNoYQENTgaOlhtMTkqna2LfbUL5ZsoE0,4274
|
|
15
|
+
cli/commands/list_command.py,sha256=CJLYVq8A2NB6Rc0_-spGONGUzB84l-0bAveJB4MjHyM,1005
|
|
16
|
+
cli/commands/version_command.py,sha256=2G5sECxLcXlOkavt61wMnt8qicv7dRFXJh3RjXzX9ic,702
|
|
17
|
+
components/__init__.py,sha256=WVuBkH-RaNkLbl-DeUv-xJlpcqB0Grvs0u6JSgJLh70,75
|
|
18
|
+
components/base_component.py,sha256=nOIz2x3LfBE7IPAxSDsqKJz9ar1TK02gK0RAISLBmGg,959
|
|
19
|
+
components/component_context.py,sha256=GIbxc02z7nZWY6k7TTPLCW5Xw6F4qwchdXL-zfEhh1E,684
|
|
20
|
+
components/component_installer.py,sha256=pC_ouPWKClWma4lHyGvXmNqn-W1RYKN8paxYvzrZxLs,1565
|
|
21
|
+
components/component_manifest.py,sha256=eazybMxZP83cffC4ZBKbL6pw9KB_Yv0IBATdaBpXlyc,3450
|
|
22
|
+
components/component_metadata.py,sha256=VPCB8inRJZ5cf20FrPb1r_dih8aVXGWSzwRkqlUvjnA,1442
|
|
23
|
+
components/component_registry.py,sha256=5nHV395XZ28tWrHlN-Q__v0e5BPIcSs01J9tSLC-N5k,2583
|
|
24
|
+
components/component_state.py,sha256=lWPQ6Lp5RCe2emPbdF-_o57SC6X__D_nnPNgmaLR2Ak,8493
|
|
25
|
+
components/component_validation.py,sha256=G01qnZQxRsZ-dZZRv2XBEMzGRvV1EfpDKYHTlImpdk0,2529
|
|
26
|
+
components/github_actions_component.py,sha256=iJQ6Zko8P9jpceR-kLFuMb7mSQFTbSgP-UoIl4v_0Po,2353
|
|
27
|
+
components/pytest_component.py,sha256=4osYfeqJNwPNyHaf-geSICxCaQW_Xln4dZfIa5vNUyU,1267
|
|
28
|
+
components/ruff_component.py,sha256=doBSCSkBS2tAhZmoFa9FUY6M8nkje5DP5qaZEN_Quek,1269
|
|
29
|
+
config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
+
config/default_structure.py,sha256=0CvLUkvuau3oD0EVDTx8MyQSBPCkj66QiOr7eXD5tk8,239
|
|
31
|
+
config/user_config.py,sha256=dPoj_rT_uWyKrE2x8n503VW5VJLFMOFuSbFeyhW5qKg,6324
|
|
32
|
+
config/version.py,sha256=GNbZRpnwmOdVGz-W3YD_6OcNpGTk9pTCzw36skpoxZY,298
|
|
33
|
+
core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
core/environment_builder.py,sha256=_2BFYiob1ysXqNMqkXiwoeuaAjeewlsF5p3qM8ZP5U0,804
|
|
35
|
+
core/git_builder.py,sha256=VDL5dVRi4FjnExWTVvj598J8zkCqvWbME5c1ZvIzdCw,2352
|
|
36
|
+
core/project_generator.py,sha256=zuTcXsmw6yospLy-Gw_MgWPzlnYhRJ5xS9nVHW-fOEA,3436
|
|
37
|
+
core/requirements_installer.py,sha256=ogrPihmuaY9eSJ0IZTpQApY2B88xHEB0WViYwnzn4kE,1424
|
|
38
|
+
core/vscode_builder.py,sha256=sYD3Gu-3RVzLDezv-M_NuUsy43Iqy0Mg883-tII-QIg,2267
|
|
39
|
+
forgepy_cli-1.0.0.dist-info/licenses/LICENSE,sha256=B5ugZWNQROTsTHRoWA5qYURkUwuYVFUKqUKQtEQzneA,1066
|
|
40
|
+
models/__init__.py,sha256=Mu55KWWsOYHH8rrzhilWR3bq2QunBzhK_P1bXnqGdIY,52
|
|
41
|
+
models/project_config.py,sha256=fn7UsE7ktKk9bGZFKgRoYBOtOC_GT56p-c4aQNLJbVQ,2848
|
|
42
|
+
templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
|
+
templates/app_template.py,sha256=ES6ByNI-qpaeAZKdwFAdZDunzwFzKCoh2D0dIF1hSvs,305
|
|
44
|
+
templates/changelog_template.py,sha256=byHUpbVgayl4_hBqalB3oPnbZWFgZrQErLlbblaNazI,245
|
|
45
|
+
templates/env_template.py,sha256=XZ0Qa8TaR5d3vKsrcuewupD5oVv0bsVdEh8a2u4Q8WI,323
|
|
46
|
+
templates/gitignore_template.py,sha256=eXGaeNSvT0Ecxcyrw-Aq1Sc7kYPk9_Hl10Xf6VSUHjI,273
|
|
47
|
+
templates/license_template.py,sha256=7VfxquTVn1TT8vC5pVzh1RxbWWdSwouD3tI3mU8yAgg,643
|
|
48
|
+
templates/pyproject_template.py,sha256=eyzv4SCle7tWCdPnCPL78rHIQ_mZR_-g1cY1bkQc04U,298
|
|
49
|
+
templates/readme_template.py,sha256=IbJ3756guBgDC7igTBeHRiCLaatALm5vYJLV8llH_9w,250
|
|
50
|
+
templates/requirements_template.py,sha256=i5-PI3tESKp25jwD_hv7m9ouHvHxVQzuOIhg8_EgEeA,214
|
|
51
|
+
templates/template_manager.py,sha256=wjHhdj3FLs6AyHL0PUKRDSyaw4mhIS9jzpt7wKoCEAs,1353
|
|
52
|
+
templates/basic/basic_files.py,sha256=vAw46wnTXZ5WFcfGHFlTPxZQh3TXYB3g2F4kRl0Hc1c,903
|
|
53
|
+
templates/basic/basic_template.py,sha256=iVlagxTfSfDjmZwuixvDQ9qn6psVa-s4tC9Dzh4LK1o,989
|
|
54
|
+
templates/cli/cli_files.py,sha256=i6zxYap2z-hSohOdjai99lJ9wmwYPUvozKRw2PjN_EA,1794
|
|
55
|
+
templates/cli/cli_template.py,sha256=DsQ2gkwiBikBvv4sd8nryLHVp7NC17V3jjCaLrEi1yg,1694
|
|
56
|
+
templates/library/library_files.py,sha256=xaFk0o_ZH-EVk4Z84AVMXagDwbEpqUJJ_QPEA2ROI7I,800
|
|
57
|
+
templates/library/library_template.py,sha256=5U7rNa6OPMDh_Ura2Mr_xgg0oxV2u5K9cVLH_o5edpQ,1681
|
|
58
|
+
templates/template_engine/base_template.py,sha256=HliAH73pBjdImlVjQgdz5sd8Ul4MvXkAE0JlxAbZXRI,1251
|
|
59
|
+
templates/template_engine/file_template.py,sha256=BBVezXHxXLqqKbvU_NSEgJEncggHUjTpzu25GLQ37rQ,2366
|
|
60
|
+
templates/template_engine/package_name.py,sha256=a04IsqwEH7JFFoOTdWc05LVzbzPe0S5LbJLTdz60XVs,652
|
|
61
|
+
templates/template_engine/template_context.py,sha256=LctJePzdwZvJhefWEULDhPfU3ilkIPd0Scb4sOjZeS8,671
|
|
62
|
+
templates/template_engine/template_files.py,sha256=GIS6dXyfj8okXo_GpoZv-V6NPuP827GsZfBKSE59c5k,261
|
|
63
|
+
templates/template_engine/template_metadata.py,sha256=k6YYRegXjfZIXSVfnYvzpEpgDvR-4hLOs6rYCSvAaB8,1547
|
|
64
|
+
templates/template_engine/template_registry.py,sha256=LzDPKcAsSNHNx172hV4O_xFKE-3mcmCraZWXb9-Npdo,3051
|
|
65
|
+
templates/vscode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
|
+
templates/vscode/extensions_template.py,sha256=uK8T_O_IhOHUN5qD-_P43nAFZpv8nkf6ugDieBCFFF8,642
|
|
67
|
+
templates/vscode/launch_template.py,sha256=UsutOs-jvusU0PcHtOk4q1yeTYlvi9bU2aRefw6r8eE,956
|
|
68
|
+
templates/vscode/settings_template.py,sha256=grpNQmkTnBNzM0zjL_2XkTTBlNICZgYMPZqGjFC9WN4,712
|
|
69
|
+
templates/vscode/tasks_template.py,sha256=DKoG1NlQ3uVFMS0a8nGUYYRzxqap35krnUKJ2bxC_y8,1570
|
|
70
|
+
forgepy_cli-1.0.0.dist-info/METADATA,sha256=NhIT-r1jNlHsvMrKn0oKyHbcRkvg5feD7nmAB8ptcsg,6762
|
|
71
|
+
forgepy_cli-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
72
|
+
forgepy_cli-1.0.0.dist-info/entry_points.txt,sha256=HXynpYTvHhzdaIFpEUkP2LBo9KYb04rnipn1yeiJb_k,38
|
|
73
|
+
forgepy_cli-1.0.0.dist-info/top_level.txt,sha256=RPhRDjNEazNrrMCb2uOUBYt6i1jnM3o2cVQuYlCFpaQ,58
|
|
74
|
+
forgepy_cli-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rendy Zou
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
main.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Module : Application Entry Point
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from cli.dispatcher import Dispatcher
|
|
9
|
+
from cli.parser import Parser
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main() -> int:
|
|
13
|
+
parser = Parser()
|
|
14
|
+
args = parser.parse()
|
|
15
|
+
|
|
16
|
+
return Dispatcher().dispatch(args)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if __name__ == "__main__":
|
|
20
|
+
raise SystemExit(main())
|
models/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Project data models distributed with ForgePy."""
|
models/project_config.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from pathlib import Path, PureWindowsPath
|
|
3
|
+
import unicodedata
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
WINDOWS_INVALID_NAME_CHARACTERS = frozenset('<>:"/\\|?*')
|
|
7
|
+
WINDOWS_RESERVED_NAME_STEMS = frozenset(
|
|
8
|
+
{"CON", "PRN", "AUX", "NUL"}
|
|
9
|
+
| {f"COM{number}" for number in range(1, 10)}
|
|
10
|
+
| {f"LPT{number}" for number in range(1, 10)}
|
|
11
|
+
| {f"COM{number}" for number in ("¹", "²", "³")}
|
|
12
|
+
| {f"LPT{number}" for number in ("¹", "²", "³")}
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(slots=True)
|
|
17
|
+
class ProjectConfig:
|
|
18
|
+
"""
|
|
19
|
+
Menyimpan konfigurasi project.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
name: str
|
|
23
|
+
location: Path
|
|
24
|
+
|
|
25
|
+
def __post_init__(self) -> None:
|
|
26
|
+
"""Validate the name used to construct the project destination."""
|
|
27
|
+
|
|
28
|
+
if not isinstance(self.name, str):
|
|
29
|
+
raise TypeError("Project name must be a string.")
|
|
30
|
+
|
|
31
|
+
if not self.name:
|
|
32
|
+
raise ValueError("Project name must not be empty.")
|
|
33
|
+
|
|
34
|
+
if not self.name.strip():
|
|
35
|
+
raise ValueError("Project name must not contain only whitespace.")
|
|
36
|
+
|
|
37
|
+
if self.name.startswith(" "):
|
|
38
|
+
raise ValueError("Project name must not begin with an ASCII space.")
|
|
39
|
+
|
|
40
|
+
if self.name in {".", ".."}:
|
|
41
|
+
raise ValueError("Project name must be one filesystem path segment.")
|
|
42
|
+
|
|
43
|
+
if any(
|
|
44
|
+
character in WINDOWS_INVALID_NAME_CHARACTERS
|
|
45
|
+
for character in self.name
|
|
46
|
+
):
|
|
47
|
+
raise ValueError(
|
|
48
|
+
"Project name contains a Windows-invalid filename character."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if any(
|
|
52
|
+
unicodedata.category(character) == "Cc"
|
|
53
|
+
for character in self.name
|
|
54
|
+
):
|
|
55
|
+
raise ValueError("Project name must not contain control characters.")
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
self.name.encode("utf-8")
|
|
59
|
+
except UnicodeEncodeError as error:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
"Project name must be valid Unicode for UTF-8 output."
|
|
62
|
+
) from error
|
|
63
|
+
|
|
64
|
+
if self.name.endswith((" ", ".")):
|
|
65
|
+
raise ValueError("Project name must not end with a space or dot.")
|
|
66
|
+
|
|
67
|
+
reserved_stem = self.name.split(".", maxsplit=1)[0].upper()
|
|
68
|
+
if reserved_stem in WINDOWS_RESERVED_NAME_STEMS:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
"Project name must not use a Windows reserved device name."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
path_name = Path(self.name)
|
|
74
|
+
windows_name = PureWindowsPath(self.name)
|
|
75
|
+
|
|
76
|
+
if path_name.is_absolute() or windows_name.is_absolute():
|
|
77
|
+
raise ValueError("Project name must not be an absolute path.")
|
|
78
|
+
|
|
79
|
+
if windows_name.drive:
|
|
80
|
+
raise ValueError("Project name must not be drive-qualified.")
|
|
81
|
+
|
|
82
|
+
if len(path_name.parts) != 1 or len(windows_name.parts) != 1:
|
|
83
|
+
raise ValueError("Project name must be one filesystem path segment.")
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def root(self) -> Path:
|
|
87
|
+
return self.location / self.name
|
templates/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
App Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_app(project_name: str) -> str:
|
|
10
|
+
return f'''"""
|
|
11
|
+
{project_name}
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
print("Welcome to {project_name}")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
main()
|
|
20
|
+
'''
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Generated file mapping owned by the basic template."""
|
|
2
|
+
|
|
3
|
+
from templates.template_manager import TemplateManager
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BasicFiles:
|
|
7
|
+
"""Build the established basic-template file mapping."""
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def build(project_name: str) -> dict[str, str]:
|
|
11
|
+
template_manager = TemplateManager()
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
"README.md": template_manager.get_readme(project_name),
|
|
15
|
+
".gitignore": template_manager.get_gitignore(),
|
|
16
|
+
"requirements.txt": template_manager.get_requirements(),
|
|
17
|
+
"app.py": template_manager.get_app(project_name),
|
|
18
|
+
"LICENSE": template_manager.get_license(),
|
|
19
|
+
"CHANGELOG.md": template_manager.get_changelog(),
|
|
20
|
+
".env": template_manager.get_env(),
|
|
21
|
+
".env.example": template_manager.get_env_example(),
|
|
22
|
+
"pyproject.toml": template_manager.get_pyproject(project_name),
|
|
23
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Basic Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from config.default_structure import DEFAULT_FOLDERS
|
|
9
|
+
from templates.basic.basic_files import BasicFiles
|
|
10
|
+
from templates.template_engine.file_template import FileTemplate
|
|
11
|
+
from templates.template_engine.template_context import TemplateContext
|
|
12
|
+
from templates.template_engine.template_metadata import TemplateMetadata
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BasicTemplate(FileTemplate):
|
|
16
|
+
|
|
17
|
+
_METADATA = TemplateMetadata(
|
|
18
|
+
name="basic",
|
|
19
|
+
description="Basic Python project starter template.",
|
|
20
|
+
version="0.6.0",
|
|
21
|
+
author="Rendy Zou",
|
|
22
|
+
tags=("python", "basic"),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
_DEFAULT_VSCODE_ENTRY_POINT = "app.py"
|
|
26
|
+
|
|
27
|
+
def _folders(self, context: TemplateContext) -> tuple[str, ...]:
|
|
28
|
+
del context
|
|
29
|
+
return tuple(DEFAULT_FOLDERS)
|
|
30
|
+
|
|
31
|
+
def _files(self, context: TemplateContext) -> dict[str, str]:
|
|
32
|
+
return BasicFiles.build(context.project_name)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Changelog Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_changelog() -> str:
|
|
10
|
+
return """# Changelog
|
|
11
|
+
|
|
12
|
+
## v1.0.0
|
|
13
|
+
|
|
14
|
+
- Initial project created by ForgePy
|
|
15
|
+
"""
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from templates.template_manager import TemplateManager
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
_GENERATED_APP_VERSION = "0.1.0"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CliFiles:
|
|
8
|
+
"""Build the minimal file mapping for a command-line application."""
|
|
9
|
+
|
|
10
|
+
@staticmethod
|
|
11
|
+
def build(
|
|
12
|
+
project_name: str,
|
|
13
|
+
package_name: str,
|
|
14
|
+
) -> dict[str, str]:
|
|
15
|
+
template_manager = TemplateManager()
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
"README.md": template_manager.get_readme(project_name),
|
|
19
|
+
".gitignore": template_manager.get_gitignore(),
|
|
20
|
+
"requirements.txt": "",
|
|
21
|
+
"pyproject.toml": template_manager.get_pyproject(project_name),
|
|
22
|
+
f"{package_name}/__init__.py": "",
|
|
23
|
+
f"{package_name}/__main__.py": CliFiles._build_main(),
|
|
24
|
+
f"{package_name}/cli.py": CliFiles._build_cli(package_name),
|
|
25
|
+
"tests/__init__.py": "",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def _build_main() -> str:
|
|
30
|
+
return '''"""Run the generated command-line application as a module."""
|
|
31
|
+
|
|
32
|
+
from .cli import main
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
raise SystemExit(main())
|
|
37
|
+
'''
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def _build_cli(package_name: str) -> str:
|
|
41
|
+
return f'''"""Command-line interface for the generated application."""
|
|
42
|
+
|
|
43
|
+
import argparse
|
|
44
|
+
from collections.abc import Sequence
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
APP_VERSION = "{_GENERATED_APP_VERSION}"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
51
|
+
parser = argparse.ArgumentParser(
|
|
52
|
+
prog={package_name!r},
|
|
53
|
+
description="Minimal command-line application generated by ForgePy.",
|
|
54
|
+
)
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"--version",
|
|
57
|
+
action="version",
|
|
58
|
+
version=f"%(prog)s {{APP_VERSION}}",
|
|
59
|
+
)
|
|
60
|
+
return parser
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
64
|
+
build_parser().parse_args(argv)
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
raise SystemExit(main())
|
|
70
|
+
'''
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from templates.cli.cli_files import CliFiles
|
|
4
|
+
from templates.template_engine.file_template import FileTemplate
|
|
5
|
+
from templates.template_engine.package_name import normalize_package_name
|
|
6
|
+
from templates.template_engine.template_context import TemplateContext
|
|
7
|
+
from templates.template_engine.template_metadata import TemplateMetadata
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CliTemplate(FileTemplate):
|
|
11
|
+
"""Generate a minimal standard-library command-line application."""
|
|
12
|
+
|
|
13
|
+
_METADATA = TemplateMetadata(
|
|
14
|
+
name="cli",
|
|
15
|
+
description="Minimal command-line application template.",
|
|
16
|
+
version="0.1.0",
|
|
17
|
+
author="Rendy Zou",
|
|
18
|
+
tags=("python", "cli", "argparse"),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
_DEFAULT_VSCODE_ENTRY_POINT = None
|
|
22
|
+
|
|
23
|
+
def _build_context(
|
|
24
|
+
self,
|
|
25
|
+
project_path: Path,
|
|
26
|
+
) -> TemplateContext:
|
|
27
|
+
return TemplateContext(
|
|
28
|
+
project_path=project_path,
|
|
29
|
+
package_name=self._normalize_package_name(project_path.name),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def _folders(self, context: TemplateContext) -> tuple[str, ...]:
|
|
33
|
+
return (
|
|
34
|
+
context.require_package_name(),
|
|
35
|
+
"tests",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def _files(self, context: TemplateContext) -> dict[str, str]:
|
|
39
|
+
return CliFiles.build(
|
|
40
|
+
project_name=context.project_name,
|
|
41
|
+
package_name=context.require_package_name(),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def _vscode_entry_point_for(
|
|
45
|
+
self,
|
|
46
|
+
context: TemplateContext,
|
|
47
|
+
) -> str | None:
|
|
48
|
+
return f"{context.require_package_name()}/cli.py"
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def _normalize_package_name(project_name: str) -> str:
|
|
52
|
+
return normalize_package_name(
|
|
53
|
+
project_name,
|
|
54
|
+
package_label="CLI",
|
|
55
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
Environment Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_env() -> str:
|
|
10
|
+
return """# Environment Variables
|
|
11
|
+
|
|
12
|
+
DEBUG=True
|
|
13
|
+
APP_NAME=MyApplication
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_env_example() -> str:
|
|
18
|
+
return """DEBUG=True
|
|
19
|
+
APP_NAME=MyApplication
|
|
20
|
+
"""
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
==================================================
|
|
3
|
+
ForgePy
|
|
4
|
+
GitIgnore Template
|
|
5
|
+
==================================================
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
def get_gitignore() -> str:
|
|
9
|
+
return """__pycache__/
|
|
10
|
+
*.pyc
|
|
11
|
+
*.pyo
|
|
12
|
+
*.pyd
|
|
13
|
+
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
|
|
17
|
+
.vscode/
|
|
18
|
+
|
|
19
|
+
.env
|
|
20
|
+
|
|
21
|
+
.idea/
|
|
22
|
+
|
|
23
|
+
dist/
|
|
24
|
+
build/
|
|
25
|
+
|
|
26
|
+
*.log
|
|
27
|
+
"""
|