cmdpackage 0.1.3__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.
- cmdpackage-0.1.3.dist-info/METADATA +211 -0
- cmdpackage-0.1.3.dist-info/RECORD +17 -0
- cmdpackage-0.1.3.dist-info/WHEEL +5 -0
- cmdpackage-0.1.3.dist-info/entry_points.txt +2 -0
- cmdpackage-0.1.3.dist-info/licenses/LICENSE +21 -0
- cmdpackage-0.1.3.dist-info/top_level.txt +1 -0
- src/__init__.py +73 -0
- src/defs/__init__.py +0 -0
- src/defs/createzVirtualEnv.py +27 -0
- src/defs/runSubProc.py +10 -0
- src/defs/writeCLIPackage.py +98 -0
- src/defs/writePyProject.py +140 -0
- src/defs/writeSetup.py +124 -0
- src/templates/__init__.py +0 -0
- src/templates/cmdTemplate.py +772 -0
- src/templates/pyprojectTemplate.py +114 -0
- src/templates/setupTemplates.py +113 -0
@@ -0,0 +1,211 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: cmdpackage
|
3
|
+
Version: 0.1.3
|
4
|
+
Summary: Automated python package generator for CLI function calls
|
5
|
+
Author-email: mpytel <mpytel@domain.com>
|
6
|
+
Maintainer-email: mpytel <mpytel@domain.com>
|
7
|
+
Project-URL: Homepage, https://github.com/mpytel/cmdpackage
|
8
|
+
Project-URL: Bug Tracker, https://github.com/mpytel/cmdpackage/issues
|
9
|
+
Classifier: Programming Language :: Python
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
11
|
+
Classifier: Operating System :: OS Independent
|
12
|
+
Description-Content-Type: text/markdown
|
13
|
+
License-File: LICENSE
|
14
|
+
Dynamic: license-file
|
15
|
+
|
16
|
+
# Command Package (cmdpackage)
|
17
|
+
|
18
|
+
## Table of Contents
|
19
|
+
|
20
|
+
- [Command Package (cmdpackage)](#command-package-cmdpackage)
|
21
|
+
- [Table of Contents](#table-of-contents)
|
22
|
+
- [Introduction](#introduction)
|
23
|
+
- [Standard Installation from PyPI](#standard-installation-from-pypi)
|
24
|
+
- [Using cmdpackage to Create New Python Packages](#using-cmdpackage-to-create-new-python-packages)
|
25
|
+
- [Modifying New Python Packages](#modifying-new-python-packages)
|
26
|
+
- [Install New Editable Python Package](#install-new-editable-python-package)
|
27
|
+
- [Editable cmdpackage Installation](#editable-cmdpackage-installation)
|
28
|
+
- [Automated Default New Package Setup with `cmdpackage.sh`](#automated-default-new-package-setup-with-cmdpackagesh)
|
29
|
+
|
30
|
+
-----
|
31
|
+
|
32
|
+
## Introduction
|
33
|
+
|
34
|
+
**cmdpackage** is a pip package that generates opinionated Python command-line program packages. These derived program packages automate the creation of new CLI program commands that you can modify in a development environment. `cmdpackage` can be installed as a standard (or "production") installation or as an editable (or "development") installation directly from GitHub.
|
35
|
+
|
36
|
+
-----
|
37
|
+
|
38
|
+
## Standard Installation from PyPI
|
39
|
+
|
40
|
+
A standard installation allows a user to quickly create derived program packages.
|
41
|
+
|
42
|
+
```bash
|
43
|
+
pip install cmdpackage
|
44
|
+
```
|
45
|
+
|
46
|
+
The derived program packages will be modified in a virtual Python development environment using `virtualenv`. This virtual environment is isolated as a self-contained Python installation with its own `site-packages` directory. This isolation prevents dependency conflicts between different projects.
|
47
|
+
|
48
|
+
```bash
|
49
|
+
pip install virtualenv
|
50
|
+
```
|
51
|
+
|
52
|
+
### Using cmdpackage to Create New Python Packages
|
53
|
+
|
54
|
+
Once installed, you can create a package directory for your new Python package (e.g., `myTypes`) and run `cmdpackage` to generate the necessary files within this directory.
|
55
|
+
|
56
|
+
```
|
57
|
+
mkdir $HOME/myTypes
|
58
|
+
cd $HOME/myTypes
|
59
|
+
cmdpackage
|
60
|
+
```
|
61
|
+
|
62
|
+
You will be asked standard package generation questions:
|
63
|
+
|
64
|
+
```
|
65
|
+
name (myTypes):
|
66
|
+
version (0.1.0):
|
67
|
+
description (myTypes pip package):
|
68
|
+
readme ():
|
69
|
+
license (MIT License):
|
70
|
+
authors (username):
|
71
|
+
authorsEmail (username@domain.com):
|
72
|
+
maintainers (username):
|
73
|
+
maintainersEmail (username@domain.com):
|
74
|
+
classifiers ():
|
75
|
+
commit a git repo [Y/n]?:
|
76
|
+
```
|
77
|
+
|
78
|
+
The **default input** for each question is the bracketed value. The username will be set to your global Git config name if Git is present; otherwise, your system login username will be used.
|
79
|
+
|
80
|
+
### Modifying New Python Packages
|
81
|
+
|
82
|
+
You're now ready to work on your new Python package, `myTypes`, by first activating a Python virtual development environment.
|
83
|
+
|
84
|
+
* **Activation Process:** To use a virtual environment, you must **activate** it. This is typically done by running a script within the environment's directory:
|
85
|
+
|
86
|
+
* On Linux/macOS: `source /path/to/myTypes/env/bin/activate`
|
87
|
+
* On Windows: `/path/to/myTypes/env/Scripts/activate` **(Not tested on Windows)**
|
88
|
+
|
89
|
+
Activating the environment modifies your terminal's shell session, primarily by adjusting your `PATH` environment variable. This ensures that when you type `python` or `pip`, you are using the Python interpreter and package manager from within that specific virtual environment, rather than your system's global Python.
|
90
|
+
|
91
|
+
**Deactivation Process:** When you **deactivate** a virtual environment, you return to the System/Global Python Environment.
|
92
|
+
|
93
|
+
* Execute: `deactivate`
|
94
|
+
|
95
|
+
#### Install New Editable Python Package
|
96
|
+
|
97
|
+
```
|
98
|
+
pip install -e .
|
99
|
+
```
|
100
|
+
|
101
|
+
Executing `pip list` results in the following output, showing the localized package installation in the Python virtual environment:
|
102
|
+
|
103
|
+
```
|
104
|
+
Package Version Editable project location
|
105
|
+
---------- ------- -----------------------------------
|
106
|
+
myTypes 0.1.0 /Users/<username>/proj/python/myTypes
|
107
|
+
pip 25.1.1
|
108
|
+
setuptools 80.9.0
|
109
|
+
wheel 0.45.1
|
110
|
+
```
|
111
|
+
|
112
|
+
Your new `myTypes` program is now ready to use as a launching point for your Python CLI development. A list of installed commands can be found using:
|
113
|
+
|
114
|
+
```
|
115
|
+
myTypes -h
|
116
|
+
```
|
117
|
+
|
118
|
+
To add a new command named `type` – which will record a token, a title representing the type of information this token represents, and a short description of what it is and where it can be used – use the following command:
|
119
|
+
|
120
|
+
```
|
121
|
+
myTypes newCmd type token title sd
|
122
|
+
```
|
123
|
+
|
124
|
+
You will then be prompted to enter help text for the command and each of the arguments you defined (`type`, `title`, `sd`):
|
125
|
+
|
126
|
+
1. Enter a description of the 'type' command:
|
127
|
+
*Records a token that represents a type of data or information.*
|
128
|
+
2. Enter a description of the 'token' argument:
|
129
|
+
*The token that represents a type of data or information.*
|
130
|
+
3. Enter a description of the 'title' argument:
|
131
|
+
*The title of the token that represents a type of data or information.*
|
132
|
+
4. Enter a description of the 'sd' argument:
|
133
|
+
*A short description of the type of data or information is entered for sd.*
|
134
|
+
|
135
|
+
Run the new `myTypes type` command:
|
136
|
+
|
137
|
+
```
|
138
|
+
myTypes type int integer 'An integer is a whole number that can be positive, negative, or zero.'
|
139
|
+
```
|
140
|
+
|
141
|
+
The path to the Python file that needs your modification (`type.py`) is displayed as output, along with the values of the three arguments (type, title, sd):
|
142
|
+
|
143
|
+
```
|
144
|
+
DEBUG: Modify default behavior in myTypes/commands/type.py
|
145
|
+
INFO: token: int
|
146
|
+
INFO: title: integer
|
147
|
+
INFO: sd: An integer is a whole number that can be positive, negative, or zero.
|
148
|
+
```
|
149
|
+
|
150
|
+
The `rmCmd` is used to remove a command from your `myTypes` program.
|
151
|
+
|
152
|
+
```
|
153
|
+
myTypes rmCmd run
|
154
|
+
```
|
155
|
+
|
156
|
+
-----
|
157
|
+
|
158
|
+
## Editable cmdpackage Installation
|
159
|
+
|
160
|
+
Modifying a clone of the `cmdpackage` GitHub repository allows a developer to change the initial files of new commands added to `cmdpackage` derived Python packages. The following commands are used to clone and install `cmdpackage` in a virtual environment for this purpose:
|
161
|
+
|
162
|
+
```bash
|
163
|
+
mkdir $HOME/proj
|
164
|
+
cd $HOME/proj
|
165
|
+
git clone https://github.com/mpytel/cmdpackage.git
|
166
|
+
cd cmdpackage
|
167
|
+
pip install -e .
|
168
|
+
```
|
169
|
+
|
170
|
+
We purposely installed `cmdpackage` in the System/Global Python Environment. `cmdpackage` is then used to generate program packages that run in Virtual Python Environments set up by `cmdpackage` when run in a new Python package directory for modification in a development environment. This is performed using the commands described in the two above sections:
|
171
|
+
|
172
|
+
1. [Creating a new package](#using-cmdpackage-to-create-new-python-packages)
|
173
|
+
2. [Modifying new python packages](#modifying-new-python-packages)
|
174
|
+
|
175
|
+
### Automated Default New Package Setup with `cmdpackage.sh`
|
176
|
+
|
177
|
+
A shell script (`cmdpackage.sh`) is provided in the `cmdpackage` directory to automate the creation of a new package using default setup values. This script creates and changes the working directory to one named after your program package. It then creates and activates a virtual environment within this directory, and installs the `cmdpackage` pip package from the local repository. It then creates the new package and uninstalls `cmdpackage` from the new package's virtual environment. The new package is installed and run to create a 'helloWorld' command to test and illustrate the `newCmd` command that is provided with the new derived package.
|
178
|
+
|
179
|
+
From any directory, execute the following command to run this shell script:
|
180
|
+
|
181
|
+
```bash
|
182
|
+
source $HOME/proj/python/cmdpackage/cmdpackage.sh myPack
|
183
|
+
```
|
184
|
+
|
185
|
+
If you prefer not to use the `cmdpackage.sh` shell script, the manual command steps used to create/install/use a new package (`myPack`) with a new 'helloWorld' command are:
|
186
|
+
|
187
|
+
```bash
|
188
|
+
mkdir myPack
|
189
|
+
cd myPack
|
190
|
+
virtualenv env/myPack
|
191
|
+
source env/myPack/bin/activate
|
192
|
+
pip install $HOME/proj/python/cmdpackage
|
193
|
+
cmdpackage
|
194
|
+
# Press the return key 11 times to accept the default values.
|
195
|
+
pip uninstall cmdpackage
|
196
|
+
# Press the return key to accept the default value Y.
|
197
|
+
pip install -e .
|
198
|
+
myPack newCmd helloWorld greeting
|
199
|
+
# When prompted by 'Enter help description for helloWorld:'
|
200
|
+
# enter: 'Echo the greeting text.'
|
201
|
+
# When prompted by 'Enter help description for greeting:'
|
202
|
+
# enter: 'The text to echo.'
|
203
|
+
myPack helloWorld "You're ready to add and remove commands, and modify code in your myPack project!"
|
204
|
+
```
|
205
|
+
|
206
|
+
The following output results from executing `myPack helloWorld`:
|
207
|
+
|
208
|
+
```
|
209
|
+
DEBUG: Modify default behavior in myPack/commands/helloWorld.py
|
210
|
+
INFO: greeting: You're ready to add and remove commands, and modify code in your myPack project!
|
211
|
+
```
|
@@ -0,0 +1,17 @@
|
|
1
|
+
cmdpackage-0.1.3.dist-info/licenses/LICENSE,sha256=I-KXocS3ZqDVWR09w3y-b41lJG1RLsYM3SYN5Haudo0,1069
|
2
|
+
src/__init__.py,sha256=jVjBxmyRJ5OTQ9ty5aZr_FR2LOa8vnLo1Z06edN-u6s,2651
|
3
|
+
src/defs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
src/defs/createzVirtualEnv.py,sha256=ZKsf_y82pRK_ExpEcAg7LfwNl6arwBFZYwtF5ZCBCiU,1008
|
5
|
+
src/defs/runSubProc.py,sha256=J-TVMYkrsmf8UnmA5KYco6Wso2XfyNp-0QEN5P6-cZY,356
|
6
|
+
src/defs/writeCLIPackage.py,sha256=rfvQmX9CkpwUqQnMoCYCoPxEE_le78A128GQXBdzfjk,3610
|
7
|
+
src/defs/writePyProject.py,sha256=KA5p07WyXZvJ-SG70831EcE-KoK4suZanDsxrGIzp20,4582
|
8
|
+
src/defs/writeSetup.py,sha256=t3S1vR8en9rDPoMGZfBgoZpHDzCGmDsvY_6M1MnjKhk,3868
|
9
|
+
src/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
src/templates/cmdTemplate.py,sha256=6sffPLlGAYNO-KxXX9VL8AsD0FX-K3R4QSB49I2tc_0,27384
|
11
|
+
src/templates/pyprojectTemplate.py,sha256=9f-AKvbAWzHhDHvsh4Yk1TeATJi1RIecic4vrmAK5ns,1778
|
12
|
+
src/templates/setupTemplates.py,sha256=SVmUVP0anIaSGWBVby-eqDoksHMTR4C8wl31qAKCdp0,1720
|
13
|
+
cmdpackage-0.1.3.dist-info/METADATA,sha256=TP60x6NoWlNBKdruZAEMP_2f14yts_56JtGHTfRm7sQ,8865
|
14
|
+
cmdpackage-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
15
|
+
cmdpackage-0.1.3.dist-info/entry_points.txt,sha256=AMwJtkreLnhYlrSO2lms8N_aeFxXiIvryvUnIPNoADQ,40
|
16
|
+
cmdpackage-0.1.3.dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4
|
17
|
+
cmdpackage-0.1.3.dist-info/RECORD,,
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Martin Pytel
|
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 @@
|
|
1
|
+
src
|
src/__init__.py
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
import os
|
2
|
+
from .defs.writePyProject import writePyProject
|
3
|
+
from .defs.writeCLIPackage import writeCLIPackage
|
4
|
+
from .defs.createzVirtualEnv import createzVirtualEnv
|
5
|
+
import sys
|
6
|
+
from pathlib import Path
|
7
|
+
|
8
|
+
GREEN = "\033[32m"
|
9
|
+
RESET = "\033[0m"
|
10
|
+
|
11
|
+
def main():
|
12
|
+
projName = ''
|
13
|
+
askForDirChange = False
|
14
|
+
if len(sys.argv) > 1:
|
15
|
+
projName: str = sys.argv[1]
|
16
|
+
# set the working directory to cwd+projName
|
17
|
+
askForDirChange = ensure_and_cd_to_directory(projName)
|
18
|
+
else:
|
19
|
+
projName = Path(os.getcwd()).stem
|
20
|
+
fields: dict[str,str] = writePyProject()
|
21
|
+
writeCLIPackage(fields)
|
22
|
+
createzVirtualEnv(fields)
|
23
|
+
print(f'*** Activate and install {projName} virtual enviroment ***')
|
24
|
+
if askForDirChange:
|
25
|
+
print(f'{GREEN}execute{RESET}: cd {projName}')
|
26
|
+
print(f'{GREEN}execute{RESET}: . env/{projName}/bin/activate')
|
27
|
+
print(f'{GREEN}execute{RESET}: pip install -e .')
|
28
|
+
|
29
|
+
if __name__ == '__main__':
|
30
|
+
main()
|
31
|
+
|
32
|
+
def ensure_and_cd_to_directory(target_dir_name: str) -> bool:
|
33
|
+
"""
|
34
|
+
Checks if a target directory exists in the current working directory.
|
35
|
+
If it exists, changes the current working directory to it.
|
36
|
+
If it does not exist, creates the directory and then changes the working directory to it.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
target_dir_name: The name of the target directory (e.g., "my_project").
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
True if the operation was successful, False otherwise.
|
43
|
+
"""
|
44
|
+
# Get the current working directory
|
45
|
+
chkForExistingProject = False
|
46
|
+
current_cwd = os.getcwd()
|
47
|
+
current_cwd_path = Path(current_cwd)
|
48
|
+
target_path = current_cwd_path.joinpath(target_dir_name)
|
49
|
+
# Construct the full path to the target directory
|
50
|
+
try:
|
51
|
+
# Check if the directory already exists
|
52
|
+
if target_path.is_dir():
|
53
|
+
files = [i for i in target_path.iterdir()]
|
54
|
+
if len(files) == 0:
|
55
|
+
if current_cwd_path.stem != target_dir_name:
|
56
|
+
os.chdir(target_path)
|
57
|
+
theCWD = os.getcwd()
|
58
|
+
print(f"Changing working directory to: {theCWD}")
|
59
|
+
else:
|
60
|
+
print(f"Program directory exits and contains files.")
|
61
|
+
return False
|
62
|
+
else:
|
63
|
+
# Directory does not exist, create it
|
64
|
+
# os.makedirs can create intermediate directories if needed
|
65
|
+
os.makedirs(target_path)
|
66
|
+
os.chdir(target_path)
|
67
|
+
theCWD = os.getcwd()
|
68
|
+
print(f"Changing working directory to: {theCWD}")
|
69
|
+
return True
|
70
|
+
except OSError as e:
|
71
|
+
print(
|
72
|
+
f"Error: Could not process directory '{target_dir_name}'. Reason: {e}")
|
73
|
+
return False
|
src/defs/__init__.py
ADDED
File without changes
|
@@ -0,0 +1,27 @@
|
|
1
|
+
from .runSubProc import runSubProc
|
2
|
+
|
3
|
+
def createzVirtualEnv(fields: dict):
|
4
|
+
try:
|
5
|
+
yellow = "\033[33m"
|
6
|
+
reset = "\033[0m"
|
7
|
+
name = "name"
|
8
|
+
rtnCompProc = runSubProc(f'virtualenv env/{fields[name]}')
|
9
|
+
print(
|
10
|
+
f'* Source the virtual environment with: {yellow}. env/{fields[name]}/bin/activate{reset}')
|
11
|
+
print(
|
12
|
+
f'* Install "{fields[name]}" with: {yellow}pip install -e .{reset}')
|
13
|
+
print(f'* Verify install with: {yellow}pip list{reset}')
|
14
|
+
print(
|
15
|
+
f'* Restore original shell environment with: {yellow}deactivate{reset}\n')
|
16
|
+
print(
|
17
|
+
f'* Create and test first new command:\n' + \
|
18
|
+
f' {yellow}{fields[name]} newCmd firstCMD firstARG{reset}\n' + \
|
19
|
+
f' {yellow}{fields[name]} -h{reset}\n' + \
|
20
|
+
f' {yellow}{fields[name]} firstCMD firstARG{reset}\n' + \
|
21
|
+
f' {yellow}{fields[name]} rmCmd firstCMD{reset}\n')
|
22
|
+
except:
|
23
|
+
print(rtnCompProc)
|
24
|
+
pass
|
25
|
+
|
26
|
+
|
27
|
+
|
src/defs/runSubProc.py
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
from subprocess import run, DEVNULL, CompletedProcess
|
2
|
+
|
3
|
+
def runSubProc(theCmd: str, noOutput=True) -> CompletedProcess:
|
4
|
+
if noOutput:
|
5
|
+
rtnCompProc = run(theCmd, shell=True,
|
6
|
+
stdout=DEVNULL,
|
7
|
+
stderr=DEVNULL)
|
8
|
+
else:
|
9
|
+
rtnCompProc = run(theCmd, shell=True)
|
10
|
+
return rtnCompProc
|
@@ -0,0 +1,98 @@
|
|
1
|
+
import os, json
|
2
|
+
from ..templates.cmdTemplate import \
|
3
|
+
initFile, logPrintTemplate, \
|
4
|
+
commandsFileStr, commandsJsonDict, \
|
5
|
+
cmdSwitchbordFileStr, cmdOptSwitchbordFileStr, \
|
6
|
+
argParseTemplate, optSwitchesTemplate, \
|
7
|
+
newCmdStr, modCmdStr, rmCmdStr, \
|
8
|
+
newCmdTemplateStr, argDefTemplateStr
|
9
|
+
|
10
|
+
def writeCLIPackage(fields: dict):
|
11
|
+
print()
|
12
|
+
field_name = "name"
|
13
|
+
programName = fields[field_name]
|
14
|
+
# -- package dir files
|
15
|
+
## write __init__.py to package dir from str
|
16
|
+
packDir = os.path.join(os.path.abspath("."),'src')
|
17
|
+
print('packDir:', str(packDir))
|
18
|
+
chkDir(packDir)
|
19
|
+
fileName = os.path.join(packDir,"__init__.py")
|
20
|
+
with open(fileName,"w") as wf:
|
21
|
+
wf.write(initFile)
|
22
|
+
|
23
|
+
# -- defs dir files
|
24
|
+
## write logPrint.py def for def dir from template
|
25
|
+
dirName = os.path.join(packDir,"defs")
|
26
|
+
fileName = os.path.join(dirName,"logIt.py")
|
27
|
+
fileStr = logPrintTemplate.substitute(name=programName)
|
28
|
+
chkDir(dirName)
|
29
|
+
with open(fileName,"w") as wf:
|
30
|
+
wf.write(fileStr)
|
31
|
+
|
32
|
+
# -- classes dir files
|
33
|
+
#write argPars.py to class directory from template
|
34
|
+
field_name = "description"
|
35
|
+
description = fields[field_name]
|
36
|
+
dirName = os.path.join(packDir,"classes")
|
37
|
+
fileName = os.path.join(dirName,"argParse.py")
|
38
|
+
fileStr = argParseTemplate.substitute(description=description)
|
39
|
+
chkDir(dirName)
|
40
|
+
with open(fileName,"w") as wf:
|
41
|
+
wf.write(fileStr)
|
42
|
+
## write optSwitches.py to clsass dir from template
|
43
|
+
fileName = os.path.join(dirName,"optSwitches.py")
|
44
|
+
fileStr = optSwitchesTemplate.substitute(name=programName)
|
45
|
+
with open(fileName,"w") as wf:
|
46
|
+
wf.write(fileStr)
|
47
|
+
|
48
|
+
# -- commands dir files
|
49
|
+
## write commands.py Commands class file
|
50
|
+
dirName = os.path.join(packDir,"commands")
|
51
|
+
fileName = os.path.join(dirName,"commands.py")
|
52
|
+
chkDir(dirName)
|
53
|
+
with open(fileName,"w") as wf:
|
54
|
+
wf.write(commandsFileStr)
|
55
|
+
# write commands.json to commands dir from dict
|
56
|
+
fileName = os.path.join(dirName,"commands.json")
|
57
|
+
with open(fileName,"w") as wf:
|
58
|
+
cmdJson = json.dumps(commandsJsonDict,indent=2)
|
59
|
+
wf.write(cmdJson)
|
60
|
+
## write cmdSwitchbord.py to def dir from str
|
61
|
+
fileName = os.path.join(dirName,"cmdSwitchbord.py")
|
62
|
+
with open(fileName,"w") as wf:
|
63
|
+
wf.write(cmdSwitchbordFileStr)
|
64
|
+
## write cmdOptSwitchbord.py to def dir from str
|
65
|
+
fileName = os.path.join(dirName,"cmdOptSwitchbord.py")
|
66
|
+
with open(fileName,"w") as wf:
|
67
|
+
wf.write(cmdOptSwitchbordFileStr)
|
68
|
+
## write newCmd.py to commands dir from str
|
69
|
+
fileName = os.path.join(dirName,"newCmd.py")
|
70
|
+
with open(fileName,"w") as wf:
|
71
|
+
wf.write(newCmdStr)
|
72
|
+
## write rmCmd.py to commands dir from str
|
73
|
+
fileName = os.path.join(dirName,"modCmd.py")
|
74
|
+
with open(fileName,"w") as wf:
|
75
|
+
wf.write(modCmdStr)
|
76
|
+
## write rmCmd.py to commands dir from str
|
77
|
+
fileName = os.path.join(dirName,"rmCmd.py")
|
78
|
+
with open(fileName,"w") as wf:
|
79
|
+
wf.write(rmCmdStr)
|
80
|
+
|
81
|
+
# -- commands\templates dir files
|
82
|
+
## write newCmd.py template file
|
83
|
+
dirName = os.path.join(dirName, "templates")
|
84
|
+
fileName = os.path.join(dirName,"newCmd.py")
|
85
|
+
chkDir(dirName)
|
86
|
+
|
87
|
+
fileStr = "from string import Template\n"
|
88
|
+
fileStr += "from textwrap import dedent\n\n"
|
89
|
+
fileStr += f'cmdDefTemplate = Template(dedent("""{newCmdTemplateStr}\n"""))\n\n'
|
90
|
+
fileStr += f'argDefTemplate = Template(dedent("""{argDefTemplateStr}\n"""))'
|
91
|
+
with open(fileName,"w") as wf:
|
92
|
+
wf.write(fileStr)
|
93
|
+
|
94
|
+
def chkDir(dirName: str):
|
95
|
+
if not os.path.isdir(dirName):
|
96
|
+
os.makedirs(dirName, exist_ok=True)
|
97
|
+
|
98
|
+
|
@@ -0,0 +1,140 @@
|
|
1
|
+
#!/usr/bin/python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
from ..templates.pyprojectTemplate import (
|
4
|
+
pyproject_base_template, gitignore_content, classifiers_line,
|
5
|
+
classifiers_template)
|
6
|
+
from sys import version_info
|
7
|
+
from .runSubProc import runSubProc
|
8
|
+
from subprocess import Popen, PIPE
|
9
|
+
from getpass import getuser
|
10
|
+
import os
|
11
|
+
|
12
|
+
|
13
|
+
def writePyProject() -> dict[str,str]:
|
14
|
+
rtnDict = {}
|
15
|
+
fields = ['name', 'version', 'description', 'readme',
|
16
|
+
'license', 'authors', 'authorsEmail', 'maintainers', 'maintainersEmail', 'classifiers']
|
17
|
+
|
18
|
+
for field_name in fields:
|
19
|
+
default_value = default_values(field_name)
|
20
|
+
if field_name == 'description':
|
21
|
+
default_value = default_value.replace('name', rtnDict['name'])
|
22
|
+
input_msg = input_message(field_name, default_value)
|
23
|
+
input_value = get_input(input_msg, default=default_value)
|
24
|
+
rtnDict[field_name] = input_value
|
25
|
+
|
26
|
+
pyproject_content = pyproject_base_template.substitute(
|
27
|
+
name=rtnDict['name'],
|
28
|
+
version=rtnDict['version'],
|
29
|
+
description=rtnDict['description'],
|
30
|
+
readme=rtnDict['readme'],
|
31
|
+
license=rtnDict['license'],
|
32
|
+
authors=rtnDict['authors'],
|
33
|
+
authorsEmail=rtnDict['authorsEmail'],
|
34
|
+
maintainers=rtnDict['maintainers'],
|
35
|
+
maintainersEmail=rtnDict['maintainersEmail'],
|
36
|
+
classifiers=gen_classifiers()
|
37
|
+
)
|
38
|
+
|
39
|
+
with open('pyproject.toml', 'w') as pyproject_file:
|
40
|
+
write_content(pyproject_file, pyproject_content)
|
41
|
+
|
42
|
+
with_gitignore = get_input('commit a git repo [Y/n]?: ',
|
43
|
+
default='y')
|
44
|
+
if with_gitignore.lower() == 'y':
|
45
|
+
with open('.gitignore', 'w') as gitignore_file:
|
46
|
+
write_content(gitignore_file, gitignore_content)
|
47
|
+
initGitRepo()
|
48
|
+
return rtnDict
|
49
|
+
|
50
|
+
|
51
|
+
def input_message(field_name, default_value):
|
52
|
+
return u'{} ({}): '.format(field_name, default_value)
|
53
|
+
|
54
|
+
|
55
|
+
def gen_classifiers():
|
56
|
+
mayor, minor = version_info[:2]
|
57
|
+
python = "Programming Language :: Python"
|
58
|
+
local = "Programming Language :: Python :: {}.{}".format(mayor, minor)
|
59
|
+
classifiers = [python, local]
|
60
|
+
|
61
|
+
classifiers_lines = ''
|
62
|
+
for cls in classifiers:
|
63
|
+
classifiers_lines += classifiers_line.substitute(classifier=cls)
|
64
|
+
|
65
|
+
return classifiers_template.substitute(classifiers=classifiers_lines)
|
66
|
+
|
67
|
+
|
68
|
+
def initGitRepo():
|
69
|
+
rtnStr = runSubProc('ls .git')
|
70
|
+
if rtnStr.returncode != 0:
|
71
|
+
rtnStr = runSubProc(f'git init')
|
72
|
+
if rtnStr.returncode == 0:
|
73
|
+
rtnStr = runSubProc(f'git add .')
|
74
|
+
if rtnStr.returncode == 0:
|
75
|
+
rtnStr = runSubProc(f'git commit -m "inital commit"', noOutput=False)
|
76
|
+
|
77
|
+
|
78
|
+
def get_username():
|
79
|
+
'''Get git config values.'''
|
80
|
+
username = ''
|
81
|
+
|
82
|
+
# use try-catch to prevent crashes if user doesn't install git
|
83
|
+
try:
|
84
|
+
# run git config --global <key> to get username
|
85
|
+
git_command = ['git', 'config', '--global', 'user.name']
|
86
|
+
p = Popen(git_command, stdout=PIPE, stderr=PIPE)
|
87
|
+
output, err = p.communicate()
|
88
|
+
|
89
|
+
# turn stdout into unicode and strip it
|
90
|
+
username = output.decode('utf-8').strip()
|
91
|
+
|
92
|
+
# if user doesn't set global git config name, then use getuser()
|
93
|
+
if not username:
|
94
|
+
username = getuser()
|
95
|
+
except OSError:
|
96
|
+
# if git command is not found, then use getuser()
|
97
|
+
username = getuser()
|
98
|
+
|
99
|
+
return username
|
100
|
+
|
101
|
+
|
102
|
+
#fields = ['name', 'version', 'description', 'readme',
|
103
|
+
# 'license', 'author', author email, 'maintainer', 'maintainer email', 'classifiers']
|
104
|
+
def default_values(field_name):
|
105
|
+
if field_name == 'name':
|
106
|
+
rtnStr = os.path.relpath('.', '..')
|
107
|
+
rtnStr = rtnStr.replace("-", "_")
|
108
|
+
return rtnStr
|
109
|
+
if field_name == 'version':
|
110
|
+
return '0.1.0'
|
111
|
+
elif field_name == 'description':
|
112
|
+
return 'name pip package'
|
113
|
+
elif field_name == 'license':
|
114
|
+
return 'MIT License'
|
115
|
+
elif field_name == 'authors':
|
116
|
+
return get_username()
|
117
|
+
elif field_name == 'authorsEmail':
|
118
|
+
return f'{get_username()}@domain.com'
|
119
|
+
elif field_name == 'maintainers':
|
120
|
+
return get_username()
|
121
|
+
elif field_name == 'maintainersEmail':
|
122
|
+
return f'{get_username()}@domain.com'
|
123
|
+
else: return ''
|
124
|
+
|
125
|
+
def get_input(input_msg, default=None):
|
126
|
+
if version_info >= (3, 0):
|
127
|
+
input_value = input(input_msg)
|
128
|
+
else:
|
129
|
+
input_value = input_msg.encode('utf8').decode('utf8')
|
130
|
+
|
131
|
+
if input_value == '':
|
132
|
+
return default
|
133
|
+
return input_value
|
134
|
+
|
135
|
+
|
136
|
+
def write_content(file, content):
|
137
|
+
if version_info >= (3, 0):
|
138
|
+
file.write(content)
|
139
|
+
else:
|
140
|
+
file.write(content.encode('utf8'))
|