makefiles-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.
- makefiles/__init__.py +0 -0
- makefiles/_version.py +21 -0
- makefiles/cli_parser.py +80 -0
- makefiles/exceptions.py +82 -0
- makefiles/mkfile.py +121 -0
- makefiles/types.py +76 -0
- makefiles/utils/__init__.py +137 -0
- makefiles/utils/cli_io.py +81 -0
- makefiles/utils/dirwalker.py +37 -0
- makefiles/utils/fileutils.py +113 -0
- makefiles/utils/picker/__init__.py +7 -0
- makefiles/utils/picker/fzf.py +48 -0
- makefiles/utils/picker/manual.py +29 -0
- makefiles_cli-1.0.0.data/scripts/mkfile +8 -0
- makefiles_cli-1.0.0.dist-info/METADATA +69 -0
- makefiles_cli-1.0.0.dist-info/RECORD +19 -0
- makefiles_cli-1.0.0.dist-info/WHEEL +5 -0
- makefiles_cli-1.0.0.dist-info/licenses/LICENSE +674 -0
- makefiles_cli-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
import shutil
|
|
3
|
+
|
|
4
|
+
import makefiles.exceptions as exceptions
|
|
5
|
+
import makefiles.types as custom_types
|
|
6
|
+
import makefiles.utils as utils
|
|
7
|
+
import makefiles.utils.cli_io as cli_io
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _remove_path(path: pathlib.Path) -> None:
|
|
11
|
+
"""
|
|
12
|
+
Remove/Unlink `path` recursively.
|
|
13
|
+
"""
|
|
14
|
+
try:
|
|
15
|
+
if utils.isfile(path) or utils.islink(path) or utils.isbrokenlink(path):
|
|
16
|
+
path.unlink(missing_ok=True)
|
|
17
|
+
elif utils.isdir(path):
|
|
18
|
+
shutil.rmtree(path)
|
|
19
|
+
except FileNotFoundError:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def copy(
|
|
24
|
+
src: pathlib.Path,
|
|
25
|
+
*dests: pathlib.Path,
|
|
26
|
+
overwrite: bool = False,
|
|
27
|
+
parents: bool = False,
|
|
28
|
+
) -> custom_types.ExitCode:
|
|
29
|
+
"""
|
|
30
|
+
Copies a source file or symbolic link to one or more destination paths.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
src (pathlib.Path): Path to the source file. Must be a regular file or a symlink to a file.
|
|
34
|
+
*dests (pathlib.Path): One or more destination paths to copy the source to.
|
|
35
|
+
overwrite (bool, optional): If True, existing destination files will be overwritten.
|
|
36
|
+
If False (default), print an error message and be skip.
|
|
37
|
+
parents (bool, optional): If True, create parent(s) if not already exists.
|
|
38
|
+
If False (default), print an error message and skip.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
custom_types.ExitCode: Exit code 0 if all copies succeed.
|
|
42
|
+
Returns 1 if any destination exists and overwrite is False.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
makefiles.exceptions.SourceNotFoundError: If the source path does not exist.
|
|
46
|
+
makefiles.exceptions.InvalidSourceError: If the source is not a file or a symbolic link to a file.
|
|
47
|
+
"""
|
|
48
|
+
exitcode: custom_types.ExitCode = custom_types.ExitCode(0)
|
|
49
|
+
|
|
50
|
+
if not utils.exists(src):
|
|
51
|
+
raise exceptions.SourceNotFoundError(f"source {str(src)} does not exists")
|
|
52
|
+
elif not (utils.isfile(src) or utils.islinkf(src)):
|
|
53
|
+
raise exceptions.InvalidSourceError(f"source {str(src)} is not a file or a link to file")
|
|
54
|
+
|
|
55
|
+
for dest in dests:
|
|
56
|
+
if utils.exists(dest) and not overwrite:
|
|
57
|
+
cli_io.eprint(f"destination {str(dest)} already exists\n")
|
|
58
|
+
exitcode = custom_types.ExitCode(1) or exitcode
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
dest_parent: pathlib.Path = dest.parent
|
|
62
|
+
if not (utils.isdir(dest_parent) or utils.islinkd(dest_parent)) and not parents:
|
|
63
|
+
cli_io.eprint(f"parent dir {str(dest_parent)} does not exists\n")
|
|
64
|
+
exitcode = custom_types.ExitCode(1) or exitcode
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
_remove_path(dest)
|
|
68
|
+
dest_parent.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
|
|
70
|
+
shutil.copyfile(src, dest, follow_symlinks=True)
|
|
71
|
+
|
|
72
|
+
return exitcode
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def create_empty_files(
|
|
76
|
+
*paths: pathlib.Path,
|
|
77
|
+
overwrite: bool = False,
|
|
78
|
+
parents: bool = False,
|
|
79
|
+
) -> custom_types.ExitCode:
|
|
80
|
+
"""
|
|
81
|
+
Creates empty files at the specified paths, optionally overwriting existing files or directories.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
*paths (pathlib.Path): One or more paths where empty files should be created.
|
|
85
|
+
overwrite (bool, optional): If True, existing destination files will be overwritten.
|
|
86
|
+
If False (default), print an error message and be skip.
|
|
87
|
+
parents (bool, optional): If True, create parent(s) if not already exists.
|
|
88
|
+
If False (default), print an error message and skip.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
makefiles.types.ExitCode: Exit code 0 on full success.
|
|
92
|
+
Exit code 1 if any file already exists and overwrite is False.
|
|
93
|
+
"""
|
|
94
|
+
exitcode: custom_types.ExitCode = custom_types.ExitCode(0)
|
|
95
|
+
|
|
96
|
+
for path in paths:
|
|
97
|
+
if utils.exists(path) and not overwrite:
|
|
98
|
+
cli_io.eprint(f"destination {path} already exists\n")
|
|
99
|
+
exitcode = custom_types.ExitCode(1) or exitcode
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
path_parent: pathlib.Path = path.parent
|
|
103
|
+
if not (utils.isdir(path_parent) or utils.islinkd(path_parent)) and not parents:
|
|
104
|
+
cli_io.eprint(f"parent dir {str(path_parent)} does not exists\n")
|
|
105
|
+
exitcode = custom_types.ExitCode(1) or exitcode
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
_remove_path(path)
|
|
109
|
+
path_parent.mkdir(parents=True, exist_ok=True)
|
|
110
|
+
|
|
111
|
+
path.touch(exist_ok=False)
|
|
112
|
+
|
|
113
|
+
return exitcode
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
import makefiles.exceptions as exceptions
|
|
5
|
+
import makefiles.types as custom_types
|
|
6
|
+
|
|
7
|
+
FZF_DEFAULT_FLAGS: list[str] = [
|
|
8
|
+
"--style=minimal",
|
|
9
|
+
"--info=hidden",
|
|
10
|
+
"--keep-right",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def prompt(options: list[str], *, height: custom_types.NaturalNumber = custom_types.NaturalNumber(10)) -> str:
|
|
15
|
+
"""
|
|
16
|
+
Displays an interactive fuzzy-selection prompt using `fzf` and returns the user's selection.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
options (list[str]): A list of string options to display in the selection interface.
|
|
20
|
+
height (custom_types.NaturalNumber, optional): Approximate terminal height percentage for the `fzf` UI.
|
|
21
|
+
Defaults to 10. The value is interpreted as `--height=~<value>` by `fzf`.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
str: The selected option as returned by `fzf`, with trailing newline removed.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
exceptions.FZFNotFoundError: If the `fzf` binary is not found in the system PATH.
|
|
28
|
+
KeyboardInterrupt: If the user interrupts the prompt using Ctrl+C (fzf returns exit code 130).
|
|
29
|
+
exceptions.FZFError: If `fzf` exits with a non-zero return code other than 130.
|
|
30
|
+
"""
|
|
31
|
+
if not shutil.which("fzf"):
|
|
32
|
+
raise exceptions.FZFNotFoundError("`fzf` is not found in path")
|
|
33
|
+
|
|
34
|
+
options_str: str = "\n".join(options)
|
|
35
|
+
|
|
36
|
+
process: subprocess.CompletedProcess = subprocess.run(
|
|
37
|
+
["fzf", *FZF_DEFAULT_FLAGS, f"--height=~{int(height)}"],
|
|
38
|
+
input=options_str,
|
|
39
|
+
text=True,
|
|
40
|
+
capture_output=True,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
if process.returncode != 0:
|
|
44
|
+
if process.returncode == 130:
|
|
45
|
+
raise KeyboardInterrupt
|
|
46
|
+
raise exceptions.FZFError("`fzf` command returned non zero exit code")
|
|
47
|
+
|
|
48
|
+
return process.stdout.strip("\n")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import makefiles.types as custom_types
|
|
2
|
+
import makefiles.utils.cli_io as cli_io
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def prompt(options: list[str]) -> str:
|
|
6
|
+
"""
|
|
7
|
+
Displays a numbered list of options and prompts the user to select one via keyboard input.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
options (list[str]): A list of string options to present to the user for selection.
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
str: The selected option from the list, based on user input.
|
|
14
|
+
"""
|
|
15
|
+
options = sorted(options) # do not modify the parameter
|
|
16
|
+
|
|
17
|
+
for index, option in enumerate(options, start=1):
|
|
18
|
+
cli_io.print(f"[{index}]: {option}\n")
|
|
19
|
+
|
|
20
|
+
while True:
|
|
21
|
+
try:
|
|
22
|
+
cli_io.print("Choose a template: ")
|
|
23
|
+
choice: custom_types.NaturalNumber = custom_types.NaturalNumber(cli_io.input())
|
|
24
|
+
assert choice <= len(options)
|
|
25
|
+
break
|
|
26
|
+
except (ValueError, TypeError, AssertionError):
|
|
27
|
+
cli_io.eprint("Please insert a valid input\n")
|
|
28
|
+
|
|
29
|
+
return options[choice - 1]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: makefiles-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A lightweight Python utility for file creation and template generation from XDG_TEMPLATES_DIR
|
|
5
|
+
License-Expression: GPL-3.0-only
|
|
6
|
+
Project-URL: Homepage, https://github.com/Rid1FZ/makefiles-cli
|
|
7
|
+
Keywords: makefiles,mkfiles,mkfile,makefiles-cli,mkfile-cli
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# makefiles-cli - Command line interface for XDG_TEMPLATES_DIR
|
|
21
|
+
|
|
22
|
+
`makefiles-cli` is a simple commandline tool to create files and templates. It can create one or more empty files or any template defined in `XDG_TEMPLATES_DIR`. It also has support for [fzf](https://github.com/junegunn/fzf) to make it easier to find template.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Create empty files:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
mkfile example1 example2
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
List all available templates:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
mkfile --list
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Create template from any template defined in `XDG_TEMPLATES_DIR`:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
mkfile script.py --template="pyscript.py"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Create template using `fzf` as picker to pick template interactively:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
mkfile script.py --template --picker="fzf"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Run `mkfile --help` for all the available options.
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
_Requirements_:
|
|
55
|
+
|
|
56
|
+
- python3 (python3.10 or greater)
|
|
57
|
+
- pip
|
|
58
|
+
|
|
59
|
+
You can install `makefiles-cli` directly from **PyPI** using `pip`:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install makefiles-cli
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or if you love to stay on bleeding edge, install directly from github:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install git+https://github.com/Rid1FZ/makefiles-cli
|
|
69
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
makefiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
makefiles/_version.py,sha256=fo5PXsZuloQZu3LdpIFTUAXvJmY2L9N5sNGe2tvdU98,511
|
|
3
|
+
makefiles/cli_parser.py,sha256=cCpL3sR7pQkbh8J2YyNYFXYs5EYDuciLBQID7ghQbPk,2041
|
|
4
|
+
makefiles/exceptions.py,sha256=oQXWabm6iFFmSpsSZRp1Q6Fmlgl-NmKVInPQlFmrBkg,2034
|
|
5
|
+
makefiles/mkfile.py,sha256=YlOFs7mpd0deDHP-auSnmBet8AYQPk84-Eu9--d1aHY,4041
|
|
6
|
+
makefiles/types.py,sha256=f52KuUyNpKLdn2V8EWdL2zaz60zK9PDsUuVyGkdnX-w,2479
|
|
7
|
+
makefiles/utils/__init__.py,sha256=UPjxn9bLa6qtWGZGrLseGiCQPRGPPh4p8rX40KoK-nk,3870
|
|
8
|
+
makefiles/utils/cli_io.py,sha256=hWWsiE5pdyJtl4_MKK15hRckdG6YHwAbIppUWOhTHo4,2561
|
|
9
|
+
makefiles/utils/dirwalker.py,sha256=nRJaAzbXT5VOAjZMl97B7jlFo5U962YGeDd2OU476_s,1225
|
|
10
|
+
makefiles/utils/fileutils.py,sha256=9yfbbofwWSxqQhTqSstkme-hZICyCGv_DFSHNTmsIbU,4301
|
|
11
|
+
makefiles/utils/picker/__init__.py,sha256=PLvX88I1vEer2-fnM7vOPk0RnSsmg4flc-ak5qSvpq0,163
|
|
12
|
+
makefiles/utils/picker/fzf.py,sha256=pjXRyQP7s7z-Ev8zM_h6mNsRCWECKrXDUI1kdCCO1p4,1714
|
|
13
|
+
makefiles/utils/picker/manual.py,sha256=IejYYqLDUwaRVAYTKA3VHR5zDaWJFbbQzKLlmN5SPJY,957
|
|
14
|
+
makefiles_cli-1.0.0.data/scripts/mkfile,sha256=tBmA2Skc-0YD8uq_DxdLmxPLUQpGspPR1xHzQRUL968,105
|
|
15
|
+
makefiles_cli-1.0.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
16
|
+
makefiles_cli-1.0.0.dist-info/METADATA,sha256=9gtQHcMrMH866G2q21DpHhYltoOpfz1EoH1AE2RMwIo,1816
|
|
17
|
+
makefiles_cli-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
+
makefiles_cli-1.0.0.dist-info/top_level.txt,sha256=_LWvFR-XPibPQepQ4yMZlEJyTQZgOtjVo1m0V9GFcxw,10
|
|
19
|
+
makefiles_cli-1.0.0.dist-info/RECORD,,
|