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.
Files changed (74) hide show
  1. builders/__init__.py +0 -0
  2. builders/base_builder.py +17 -0
  3. builders/file_builder.py +31 -0
  4. builders/folder_builder.py +30 -0
  5. builders/python_tools_builder.py +64 -0
  6. cli/__init__.py +3 -0
  7. cli/command.py +52 -0
  8. cli/commands/__init__.py +32 -0
  9. cli/commands/component_command.py +177 -0
  10. cli/commands/config_command.py +174 -0
  11. cli/commands/create_command.py +161 -0
  12. cli/commands/list_command.py +40 -0
  13. cli/commands/version_command.py +29 -0
  14. cli/dispatcher.py +61 -0
  15. cli/parser.py +84 -0
  16. components/__init__.py +1 -0
  17. components/base_component.py +30 -0
  18. components/component_context.py +21 -0
  19. components/component_installer.py +44 -0
  20. components/component_manifest.py +102 -0
  21. components/component_metadata.py +50 -0
  22. components/component_registry.py +74 -0
  23. components/component_state.py +243 -0
  24. components/component_validation.py +88 -0
  25. components/github_actions_component.py +75 -0
  26. components/pytest_component.py +40 -0
  27. components/ruff_component.py +40 -0
  28. config/__init__.py +0 -0
  29. config/default_structure.py +18 -0
  30. config/user_config.py +238 -0
  31. config/version.py +16 -0
  32. core/__init__.py +0 -0
  33. core/environment_builder.py +33 -0
  34. core/git_builder.py +90 -0
  35. core/project_generator.py +125 -0
  36. core/requirements_installer.py +50 -0
  37. core/vscode_builder.py +103 -0
  38. forgepy_cli-1.0.0.dist-info/METADATA +176 -0
  39. forgepy_cli-1.0.0.dist-info/RECORD +74 -0
  40. forgepy_cli-1.0.0.dist-info/WHEEL +5 -0
  41. forgepy_cli-1.0.0.dist-info/entry_points.txt +2 -0
  42. forgepy_cli-1.0.0.dist-info/licenses/LICENSE +21 -0
  43. forgepy_cli-1.0.0.dist-info/top_level.txt +8 -0
  44. main.py +20 -0
  45. models/__init__.py +1 -0
  46. models/project_config.py +87 -0
  47. templates/__init__.py +0 -0
  48. templates/app_template.py +20 -0
  49. templates/basic/basic_files.py +23 -0
  50. templates/basic/basic_template.py +32 -0
  51. templates/changelog_template.py +15 -0
  52. templates/cli/cli_files.py +70 -0
  53. templates/cli/cli_template.py +55 -0
  54. templates/env_template.py +20 -0
  55. templates/gitignore_template.py +27 -0
  56. templates/library/library_files.py +28 -0
  57. templates/library/library_template.py +56 -0
  58. templates/license_template.py +21 -0
  59. templates/pyproject_template.py +15 -0
  60. templates/readme_template.py +15 -0
  61. templates/requirements_template.py +13 -0
  62. templates/template_engine/base_template.py +58 -0
  63. templates/template_engine/file_template.py +76 -0
  64. templates/template_engine/package_name.py +30 -0
  65. templates/template_engine/template_context.py +24 -0
  66. templates/template_engine/template_files.py +10 -0
  67. templates/template_engine/template_metadata.py +59 -0
  68. templates/template_engine/template_registry.py +107 -0
  69. templates/template_manager.py +58 -0
  70. templates/vscode/__init__.py +0 -0
  71. templates/vscode/extensions_template.py +32 -0
  72. templates/vscode/launch_template.py +41 -0
  73. templates/vscode/settings_template.py +37 -0
  74. 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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ forgepy = main:main
@@ -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.
@@ -0,0 +1,8 @@
1
+ builders
2
+ cli
3
+ components
4
+ config
5
+ core
6
+ main
7
+ models
8
+ templates
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."""
@@ -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
+ """