biscuitcutter 1.0.0__tar.gz
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.
- biscuitcutter-1.0.0/LICENSE +22 -0
- biscuitcutter-1.0.0/MANIFEST.in +1 -0
- biscuitcutter-1.0.0/PKG-INFO +130 -0
- biscuitcutter-1.0.0/README.md +108 -0
- biscuitcutter-1.0.0/biscuitcutter/__init__.py +42 -0
- biscuitcutter-1.0.0/biscuitcutter/biscuitcutter.py +194 -0
- biscuitcutter-1.0.0/biscuitcutter/examples.py +6 -0
- biscuitcutter-1.0.0/biscuitcutter/template.zip +0 -0
- biscuitcutter-1.0.0/biscuitcutter.egg-info/PKG-INFO +130 -0
- biscuitcutter-1.0.0/biscuitcutter.egg-info/SOURCES.txt +13 -0
- biscuitcutter-1.0.0/biscuitcutter.egg-info/dependency_links.txt +1 -0
- biscuitcutter-1.0.0/biscuitcutter.egg-info/entry_points.txt +4 -0
- biscuitcutter-1.0.0/biscuitcutter.egg-info/top_level.txt +1 -0
- biscuitcutter-1.0.0/pyproject.toml +46 -0
- biscuitcutter-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Erdogan Taskesen
|
|
4
|
+
biscuitcutter - Python package
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include biscuitcutter/template.zip
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: biscuitcutter
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: biscuitcutter – scaffold a new Python repo from a template in seconds.
|
|
5
|
+
Author-email: Erdogan Taskesen <erdogant@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://erdogant.github.io/biscuitcutter
|
|
8
|
+
Project-URL: Download, https://github.com/erdogant/biscuitcutter/archive/{version}.tar.gz
|
|
9
|
+
Keywords: Python,biscuitcutter,templates,scaffolding,cookiecutter
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Education
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: Unix
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
[](https://img.shields.io/pypi/pyversions/biscuitcutter)
|
|
24
|
+
[](https://pypi.org/project/biscuitcutter/)
|
|
25
|
+
[](https://erdogant.github.io/biscuitcutter/)
|
|
26
|
+
[](https://github.com/erdogant/biscuitcutter/)
|
|
27
|
+
[](https://pepy.tech/project/biscuitcutter)
|
|
28
|
+
[](https://pepy.tech/project/biscuitcutter)
|
|
29
|
+
[](https://github.com/erdogant/biscuitcutter/blob/master/LICENSE)
|
|
30
|
+
[](https://github.com/erdogant/biscuitcutter/network)
|
|
31
|
+
[](https://github.com/erdogant/biscuitcutter/issues)
|
|
32
|
+
[](http://www.repostatus.org/#active)
|
|
33
|
+
|
|
34
|
+
<div>
|
|
35
|
+
<a href="https://erdogant.github.io/biscuitcutter/">
|
|
36
|
+
<img src="https://raw.githubusercontent.com/erdogant/biscuitcutter/master/docs/figs/logo.png"
|
|
37
|
+
width="250"
|
|
38
|
+
align="left" />
|
|
39
|
+
</a>
|
|
40
|
+
<strong>biscuitcutter</strong> is a new repository scaffolder for Python projects.
|
|
41
|
+
|
|
42
|
+
Create a new Python package or repository with a single command! ⭐️Star it if you like it⭐️
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
### Key Features
|
|
48
|
+
|
|
49
|
+
| Feature | Description |
|
|
50
|
+
|--------|-------------|
|
|
51
|
+
| [**Interactive CLI**](https://erdogant.github.io/biscuitcutter/pages/html/Installation.html) | Scaffold a new Python repository with prompts for repo name, author info, and more. |
|
|
52
|
+
| [**Template-Based**](https://erdogant.github.io/biscuitcutter/) | Uses a pre-configured template to create a ready-to-use project structure. |
|
|
53
|
+
| [**Placeholder Replacement**](https://erdogant.github.io/biscuitcutter/) | Automatically replaces placeholders (repo name, author, email, GitHub username) in all files. |
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### Resources and Links
|
|
58
|
+
- **Documentation:** [Website](https://erdogant.github.io/biscuitcutter)
|
|
59
|
+
- **Bug Reports and Feature Requests:** [GitHub Issues](https://github.com/erdogant/biscuitcutter/issues)
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### Installation
|
|
64
|
+
|
|
65
|
+
##### Install biscuitcutter from PyPI
|
|
66
|
+
```bash
|
|
67
|
+
pip install biscuitcutter
|
|
68
|
+
```
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### Usage
|
|
72
|
+
|
|
73
|
+
After installation, run the interactive CLI:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
biscuitcutter
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This will prompt you for:
|
|
80
|
+
|
|
81
|
+
- **Repository / package name** (required)
|
|
82
|
+
- **Author full name** (required)
|
|
83
|
+
- **Author e-mail** (optional)
|
|
84
|
+
- **GitHub username** (optional, defaults to lowercase author name without spaces)
|
|
85
|
+
- **Parent directory** (optional, defaults to current directory)
|
|
86
|
+
|
|
87
|
+
Alternatively, you can import and call the main function directly:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from biscuitcutter import main
|
|
91
|
+
main()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### Example Session
|
|
97
|
+
|
|
98
|
+
```console
|
|
99
|
+
🍪 biscuitcutter – new repo scaffolder
|
|
100
|
+
|
|
101
|
+
───────────────────────────────────────────
|
|
102
|
+
Repository / package name: my_project
|
|
103
|
+
Author full name: Erdogan Taskesen
|
|
104
|
+
Author e-mail: erdogant@gmail.com
|
|
105
|
+
GitHub username: erdogant
|
|
106
|
+
Parent directory (leave blank for current dir):
|
|
107
|
+
|
|
108
|
+
✔ Created directory: /path/to/my_project
|
|
109
|
+
✔ Template extracted to: /path/to/my_project
|
|
110
|
+
✔ Placeholders replaced.
|
|
111
|
+
|
|
112
|
+
🎉 Done! Your new repo is ready at:
|
|
113
|
+
/path/to/my_project
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### Contributors
|
|
119
|
+
|
|
120
|
+
Setting up and maintaining biscuitcutter has been possible thanks to users and contributors. Thanks to:
|
|
121
|
+
|
|
122
|
+
<p align="left">
|
|
123
|
+
<a href="https://github.com/erdogant/biscuitcutter/graphs/contributors">
|
|
124
|
+
<img src="https://contrib.rocks/image?repo=erdogant/biscuitcutter" />
|
|
125
|
+
</a>
|
|
126
|
+
</p>
|
|
127
|
+
|
|
128
|
+
### Maintainer
|
|
129
|
+
* Erdogan Taskesen, github: [erdogant](https://github.com/erdogant)
|
|
130
|
+
* Contributions are welcome.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
[](https://img.shields.io/pypi/pyversions/biscuitcutter)
|
|
2
|
+
[](https://pypi.org/project/biscuitcutter/)
|
|
3
|
+
[](https://erdogant.github.io/biscuitcutter/)
|
|
4
|
+
[](https://github.com/erdogant/biscuitcutter/)
|
|
5
|
+
[](https://pepy.tech/project/biscuitcutter)
|
|
6
|
+
[](https://pepy.tech/project/biscuitcutter)
|
|
7
|
+
[](https://github.com/erdogant/biscuitcutter/blob/master/LICENSE)
|
|
8
|
+
[](https://github.com/erdogant/biscuitcutter/network)
|
|
9
|
+
[](https://github.com/erdogant/biscuitcutter/issues)
|
|
10
|
+
[](http://www.repostatus.org/#active)
|
|
11
|
+
|
|
12
|
+
<div>
|
|
13
|
+
<a href="https://erdogant.github.io/biscuitcutter/">
|
|
14
|
+
<img src="https://raw.githubusercontent.com/erdogant/biscuitcutter/master/docs/figs/logo.png"
|
|
15
|
+
width="250"
|
|
16
|
+
align="left" />
|
|
17
|
+
</a>
|
|
18
|
+
<strong>biscuitcutter</strong> is a new repository scaffolder for Python projects.
|
|
19
|
+
|
|
20
|
+
Create a new Python package or repository with a single command! ⭐️Star it if you like it⭐️
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
### Key Features
|
|
26
|
+
|
|
27
|
+
| Feature | Description |
|
|
28
|
+
|--------|-------------|
|
|
29
|
+
| [**Interactive CLI**](https://erdogant.github.io/biscuitcutter/pages/html/Installation.html) | Scaffold a new Python repository with prompts for repo name, author info, and more. |
|
|
30
|
+
| [**Template-Based**](https://erdogant.github.io/biscuitcutter/) | Uses a pre-configured template to create a ready-to-use project structure. |
|
|
31
|
+
| [**Placeholder Replacement**](https://erdogant.github.io/biscuitcutter/) | Automatically replaces placeholders (repo name, author, email, GitHub username) in all files. |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### Resources and Links
|
|
36
|
+
- **Documentation:** [Website](https://erdogant.github.io/biscuitcutter)
|
|
37
|
+
- **Bug Reports and Feature Requests:** [GitHub Issues](https://github.com/erdogant/biscuitcutter/issues)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
### Installation
|
|
42
|
+
|
|
43
|
+
##### Install biscuitcutter from PyPI
|
|
44
|
+
```bash
|
|
45
|
+
pip install biscuitcutter
|
|
46
|
+
```
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### Usage
|
|
50
|
+
|
|
51
|
+
After installation, run the interactive CLI:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
biscuitcutter
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This will prompt you for:
|
|
58
|
+
|
|
59
|
+
- **Repository / package name** (required)
|
|
60
|
+
- **Author full name** (required)
|
|
61
|
+
- **Author e-mail** (optional)
|
|
62
|
+
- **GitHub username** (optional, defaults to lowercase author name without spaces)
|
|
63
|
+
- **Parent directory** (optional, defaults to current directory)
|
|
64
|
+
|
|
65
|
+
Alternatively, you can import and call the main function directly:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from biscuitcutter import main
|
|
69
|
+
main()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### Example Session
|
|
75
|
+
|
|
76
|
+
```console
|
|
77
|
+
🍪 biscuitcutter – new repo scaffolder
|
|
78
|
+
|
|
79
|
+
───────────────────────────────────────────
|
|
80
|
+
Repository / package name: my_project
|
|
81
|
+
Author full name: Erdogan Taskesen
|
|
82
|
+
Author e-mail: erdogant@gmail.com
|
|
83
|
+
GitHub username: erdogant
|
|
84
|
+
Parent directory (leave blank for current dir):
|
|
85
|
+
|
|
86
|
+
✔ Created directory: /path/to/my_project
|
|
87
|
+
✔ Template extracted to: /path/to/my_project
|
|
88
|
+
✔ Placeholders replaced.
|
|
89
|
+
|
|
90
|
+
🎉 Done! Your new repo is ready at:
|
|
91
|
+
/path/to/my_project
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### Contributors
|
|
97
|
+
|
|
98
|
+
Setting up and maintaining biscuitcutter has been possible thanks to users and contributors. Thanks to:
|
|
99
|
+
|
|
100
|
+
<p align="left">
|
|
101
|
+
<a href="https://github.com/erdogant/biscuitcutter/graphs/contributors">
|
|
102
|
+
<img src="https://contrib.rocks/image?repo=erdogant/biscuitcutter" />
|
|
103
|
+
</a>
|
|
104
|
+
</p>
|
|
105
|
+
|
|
106
|
+
### Maintainer
|
|
107
|
+
* Erdogan Taskesen, github: [erdogant](https://github.com/erdogant)
|
|
108
|
+
* Contributions are welcome.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
# We import 'main' instead of 'biscuitcutter' since that is the actual entry point in your script.
|
|
4
|
+
from biscuitcutter.biscuitcutter import main
|
|
5
|
+
|
|
6
|
+
__author__ = 'Erdogan Taskesen'
|
|
7
|
+
__email__ = 'erdogant@gmail.com'
|
|
8
|
+
__version__ = '1.0.0'
|
|
9
|
+
|
|
10
|
+
# Setup package-level logger
|
|
11
|
+
_logger = logging.getLogger('biscuitcutter')
|
|
12
|
+
_log_handler = logging.StreamHandler()
|
|
13
|
+
_formatter = logging.Formatter(fmt='[{asctime}] [{name:<12.12}] [{levelname:<8}] {message}', style='{', datefmt='%d-%m-%Y %H:%M:%S')
|
|
14
|
+
_log_handler.setFormatter(_formatter)
|
|
15
|
+
_log_handler.setLevel(logging.DEBUG)
|
|
16
|
+
if not _logger.hasHandlers(): # avoid duplicate handlers if re-imported
|
|
17
|
+
_logger.addHandler(_log_handler)
|
|
18
|
+
_logger.setLevel(logging.DEBUG)
|
|
19
|
+
_logger.propagate = True # allow submodules to inherit this handler
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# module level doc-string
|
|
23
|
+
__doc__ = """
|
|
24
|
+
biscuitcutter
|
|
25
|
+
=====================================================================
|
|
26
|
+
|
|
27
|
+
biscuitcutter is a new repo scaffolder using command line interface.
|
|
28
|
+
|
|
29
|
+
Example
|
|
30
|
+
-------
|
|
31
|
+
>>> # Install
|
|
32
|
+
>>> pip install biscuitcutter
|
|
33
|
+
>>> # From the terminal run:
|
|
34
|
+
>>> biscuitcutter
|
|
35
|
+
>>>
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
References
|
|
39
|
+
----------
|
|
40
|
+
https://github.com/erdogant/biscuitcutter
|
|
41
|
+
|
|
42
|
+
"""
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""biscuitcutter.
|
|
2
|
+
|
|
3
|
+
Name : biscuitcutter.py
|
|
4
|
+
Author : Erdogan Taskesen
|
|
5
|
+
Contact : erdogant@gmail.com
|
|
6
|
+
github : https://github.com/erdogant/biscuitcutter
|
|
7
|
+
Licence : MIT
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
import zipfile
|
|
14
|
+
import shutil
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
TEMPLATE_PLACEHOLDER_GITHUB = "GITHUBNAME"
|
|
20
|
+
TEMPLATE_PLACEHOLDER_REPO = "REPONAME"
|
|
21
|
+
TEMPLATE_PLACEHOLDER_AUTHOR = "AUTHORNAME"
|
|
22
|
+
TEMPLATE_PLACEHOLDER_EMAIL = "CONTACTADRESS"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# CLI entry-point
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
def main():
|
|
30
|
+
"""Interactive CLI: scaffold a new Python repo from the biscuitcutter template.
|
|
31
|
+
|
|
32
|
+
Example
|
|
33
|
+
-------
|
|
34
|
+
>>> # Install
|
|
35
|
+
>>> pip install biscuitcutter
|
|
36
|
+
>>> # From the terminal run:
|
|
37
|
+
>>> biscuitcutter
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
print("\n🍪 biscuitcutter – new repo scaffolder\n" + "─" * 42)
|
|
41
|
+
|
|
42
|
+
# ── 1. Collect user input ───────────────────────────────────────────────
|
|
43
|
+
repo_name = _prompt("Repository / package name", required=True)
|
|
44
|
+
author_name = _prompt("Author full name", required=True)
|
|
45
|
+
author_email= _prompt("Author e-mail", required=False, default="")
|
|
46
|
+
github_user = _prompt("GitHub username", required=False, default=author_name.lower().replace(" ", ""))
|
|
47
|
+
|
|
48
|
+
output_dir = _prompt(
|
|
49
|
+
"Parent directory (leave blank for current dir)",
|
|
50
|
+
required=False,
|
|
51
|
+
default=os.getcwd(),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
repo_dir = os.path.join(output_dir, repo_name)
|
|
55
|
+
|
|
56
|
+
# ── 2. Create repo directory ────────────────────────────────────────────
|
|
57
|
+
if os.path.exists(repo_dir):
|
|
58
|
+
overwrite = input(f"\n⚠️ '{repo_dir}' already exists. Overwrite? [y/N]: ").strip().lower()
|
|
59
|
+
if overwrite != "y":
|
|
60
|
+
print("Aborted.")
|
|
61
|
+
sys.exit(0)
|
|
62
|
+
shutil.rmtree(repo_dir)
|
|
63
|
+
|
|
64
|
+
os.makedirs(repo_dir)
|
|
65
|
+
print(f"\n✔ Created directory: {repo_dir}")
|
|
66
|
+
|
|
67
|
+
# ── 3. Download & extract template.zip ─────────────────────────────────
|
|
68
|
+
target_directory = os.path.dirname(os.path.abspath(__file__))
|
|
69
|
+
zip_path = os.path.join(target_directory, 'template.zip')
|
|
70
|
+
print(f"directory: {target_directory}")
|
|
71
|
+
print(f"Template: {zip_path}")
|
|
72
|
+
|
|
73
|
+
_extract_zip(zip_path, repo_dir)
|
|
74
|
+
# os.remove(zip_path)
|
|
75
|
+
print(f"✔ Template extracted to: {repo_dir}")
|
|
76
|
+
|
|
77
|
+
# ── 4. Rename variables / placeholders inside all text files ───────────
|
|
78
|
+
replacements = {
|
|
79
|
+
TEMPLATE_PLACEHOLDER_REPO: repo_name,
|
|
80
|
+
TEMPLATE_PLACEHOLDER_AUTHOR: author_name,
|
|
81
|
+
TEMPLATE_PLACEHOLDER_EMAIL: author_email,
|
|
82
|
+
TEMPLATE_PLACEHOLDER_GITHUB: github_user,
|
|
83
|
+
}
|
|
84
|
+
_rename_in_tree(repo_dir, replacements)
|
|
85
|
+
_rename_paths(repo_dir, TEMPLATE_PLACEHOLDER_REPO, repo_name)
|
|
86
|
+
|
|
87
|
+
print(f"✔ Placeholders replaced.")
|
|
88
|
+
print(f"\n🎉 Done! Your new repo is ready at:\n {repo_dir}\n")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
# Helpers
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
def _prompt(label, required=True, default=None):
|
|
96
|
+
suffix = f" [{default}]" if default else ""
|
|
97
|
+
while True:
|
|
98
|
+
value = input(f" {label}{suffix}: ").strip()
|
|
99
|
+
if not value and default is not None:
|
|
100
|
+
return default
|
|
101
|
+
if not value and required:
|
|
102
|
+
print(f" ✗ '{label}' is required.")
|
|
103
|
+
continue
|
|
104
|
+
return value or ""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _create_stub_zip(dest_path, placeholder):
|
|
108
|
+
"""Create a minimal stub zip when the real template is unavailable."""
|
|
109
|
+
import io, zipfile as zf
|
|
110
|
+
|
|
111
|
+
buf = io.BytesIO()
|
|
112
|
+
with zf.ZipFile(buf, "w") as z:
|
|
113
|
+
z.writestr(f"{placeholder}/__init__.py", f"# {placeholder}\n__version__ = '0.1.0'\n")
|
|
114
|
+
z.writestr(f"{placeholder}/{placeholder}.py",
|
|
115
|
+
f'"""{placeholder}."""\n\nclass {placeholder}:\n pass\n')
|
|
116
|
+
z.writestr("README.md", f"# {placeholder}\n\nA new Python library.\n")
|
|
117
|
+
z.writestr("pyproject.toml",
|
|
118
|
+
f'[project]\nname = "{placeholder}"\n'
|
|
119
|
+
f'authors = [{{name = "Erdogan Taskesen", email = "erdogant@gmail.com"}}]\n')
|
|
120
|
+
with open(dest_path, "wb") as fh:
|
|
121
|
+
fh.write(buf.getvalue())
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _extract_zip(zip_path, dest_dir):
|
|
125
|
+
"""Extract zip; if the archive has a single top-level folder, strip it."""
|
|
126
|
+
with zipfile.ZipFile(zip_path, "r") as z:
|
|
127
|
+
members = z.namelist()
|
|
128
|
+
# Detect common prefix (GitHub zips add a top-level folder)
|
|
129
|
+
top_dirs = {m.split("/")[0] for m in members if "/" in m}
|
|
130
|
+
strip = ""
|
|
131
|
+
if len(top_dirs) == 1:
|
|
132
|
+
candidate = top_dirs.pop() + "/"
|
|
133
|
+
if all(m.startswith(candidate) or m == candidate.rstrip("/") for m in members):
|
|
134
|
+
strip = candidate
|
|
135
|
+
|
|
136
|
+
for member in members:
|
|
137
|
+
rel = member[len(strip):] if strip and member.startswith(strip) else member
|
|
138
|
+
if not rel:
|
|
139
|
+
continue
|
|
140
|
+
target = os.path.join(dest_dir, rel)
|
|
141
|
+
if member.endswith("/"):
|
|
142
|
+
os.makedirs(target, exist_ok=True)
|
|
143
|
+
else:
|
|
144
|
+
os.makedirs(os.path.dirname(target), exist_ok=True)
|
|
145
|
+
with z.open(member) as src, open(target, "wb") as dst:
|
|
146
|
+
shutil.copyfileobj(src, dst)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _rename_in_tree(root, replacements):
|
|
150
|
+
"""Walk *root* and replace placeholder strings in every text file."""
|
|
151
|
+
TEXT_EXTENSIONS = {
|
|
152
|
+
".py", ".md", ".rst", ".txt", ".toml", ".cfg", ".ini",
|
|
153
|
+
".yml", ".yaml", ".json", ".html", ".js", ".css", ".sh",
|
|
154
|
+
".bat", ".ps1", "",
|
|
155
|
+
}
|
|
156
|
+
for dirpath, _dirs, files in os.walk(root):
|
|
157
|
+
for fname in files:
|
|
158
|
+
_, ext = os.path.splitext(fname)
|
|
159
|
+
if ext.lower() not in TEXT_EXTENSIONS:
|
|
160
|
+
continue
|
|
161
|
+
fpath = os.path.join(dirpath, fname)
|
|
162
|
+
try:
|
|
163
|
+
with open(fpath, "r", encoding="utf-8", errors="replace") as fh:
|
|
164
|
+
content = fh.read()
|
|
165
|
+
new_content = content
|
|
166
|
+
for old, new in replacements.items():
|
|
167
|
+
new_content = new_content.replace(old, new)
|
|
168
|
+
if new_content != content:
|
|
169
|
+
with open(fpath, "w", encoding="utf-8") as fh:
|
|
170
|
+
fh.write(new_content)
|
|
171
|
+
except Exception as exc:
|
|
172
|
+
logger.debug(f"Skipping {fpath}: {exc}")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _rename_paths(root, old_name, new_name):
|
|
176
|
+
"""Rename files and directories that contain *old_name* in their names."""
|
|
177
|
+
# Walk bottom-up so we rename children before parents.
|
|
178
|
+
for dirpath, dirs, files in os.walk(root, topdown=False):
|
|
179
|
+
for fname in files:
|
|
180
|
+
if old_name in fname:
|
|
181
|
+
src = os.path.join(dirpath, fname)
|
|
182
|
+
dst = os.path.join(dirpath, fname.replace(old_name, new_name))
|
|
183
|
+
os.rename(src, dst)
|
|
184
|
+
for dname in dirs:
|
|
185
|
+
if old_name in dname:
|
|
186
|
+
src = os.path.join(dirpath, dname)
|
|
187
|
+
dst = os.path.join(dirpath, dname.replace(old_name, new_name))
|
|
188
|
+
os.rename(src, dst)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# ---------------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
main()
|
|
Binary file
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: biscuitcutter
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: biscuitcutter – scaffold a new Python repo from a template in seconds.
|
|
5
|
+
Author-email: Erdogan Taskesen <erdogant@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://erdogant.github.io/biscuitcutter
|
|
8
|
+
Project-URL: Download, https://github.com/erdogant/biscuitcutter/archive/{version}.tar.gz
|
|
9
|
+
Keywords: Python,biscuitcutter,templates,scaffolding,cookiecutter
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Education
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: Unix
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
[](https://img.shields.io/pypi/pyversions/biscuitcutter)
|
|
24
|
+
[](https://pypi.org/project/biscuitcutter/)
|
|
25
|
+
[](https://erdogant.github.io/biscuitcutter/)
|
|
26
|
+
[](https://github.com/erdogant/biscuitcutter/)
|
|
27
|
+
[](https://pepy.tech/project/biscuitcutter)
|
|
28
|
+
[](https://pepy.tech/project/biscuitcutter)
|
|
29
|
+
[](https://github.com/erdogant/biscuitcutter/blob/master/LICENSE)
|
|
30
|
+
[](https://github.com/erdogant/biscuitcutter/network)
|
|
31
|
+
[](https://github.com/erdogant/biscuitcutter/issues)
|
|
32
|
+
[](http://www.repostatus.org/#active)
|
|
33
|
+
|
|
34
|
+
<div>
|
|
35
|
+
<a href="https://erdogant.github.io/biscuitcutter/">
|
|
36
|
+
<img src="https://raw.githubusercontent.com/erdogant/biscuitcutter/master/docs/figs/logo.png"
|
|
37
|
+
width="250"
|
|
38
|
+
align="left" />
|
|
39
|
+
</a>
|
|
40
|
+
<strong>biscuitcutter</strong> is a new repository scaffolder for Python projects.
|
|
41
|
+
|
|
42
|
+
Create a new Python package or repository with a single command! ⭐️Star it if you like it⭐️
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
### Key Features
|
|
48
|
+
|
|
49
|
+
| Feature | Description |
|
|
50
|
+
|--------|-------------|
|
|
51
|
+
| [**Interactive CLI**](https://erdogant.github.io/biscuitcutter/pages/html/Installation.html) | Scaffold a new Python repository with prompts for repo name, author info, and more. |
|
|
52
|
+
| [**Template-Based**](https://erdogant.github.io/biscuitcutter/) | Uses a pre-configured template to create a ready-to-use project structure. |
|
|
53
|
+
| [**Placeholder Replacement**](https://erdogant.github.io/biscuitcutter/) | Automatically replaces placeholders (repo name, author, email, GitHub username) in all files. |
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### Resources and Links
|
|
58
|
+
- **Documentation:** [Website](https://erdogant.github.io/biscuitcutter)
|
|
59
|
+
- **Bug Reports and Feature Requests:** [GitHub Issues](https://github.com/erdogant/biscuitcutter/issues)
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### Installation
|
|
64
|
+
|
|
65
|
+
##### Install biscuitcutter from PyPI
|
|
66
|
+
```bash
|
|
67
|
+
pip install biscuitcutter
|
|
68
|
+
```
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### Usage
|
|
72
|
+
|
|
73
|
+
After installation, run the interactive CLI:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
biscuitcutter
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This will prompt you for:
|
|
80
|
+
|
|
81
|
+
- **Repository / package name** (required)
|
|
82
|
+
- **Author full name** (required)
|
|
83
|
+
- **Author e-mail** (optional)
|
|
84
|
+
- **GitHub username** (optional, defaults to lowercase author name without spaces)
|
|
85
|
+
- **Parent directory** (optional, defaults to current directory)
|
|
86
|
+
|
|
87
|
+
Alternatively, you can import and call the main function directly:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
from biscuitcutter import main
|
|
91
|
+
main()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### Example Session
|
|
97
|
+
|
|
98
|
+
```console
|
|
99
|
+
🍪 biscuitcutter – new repo scaffolder
|
|
100
|
+
|
|
101
|
+
───────────────────────────────────────────
|
|
102
|
+
Repository / package name: my_project
|
|
103
|
+
Author full name: Erdogan Taskesen
|
|
104
|
+
Author e-mail: erdogant@gmail.com
|
|
105
|
+
GitHub username: erdogant
|
|
106
|
+
Parent directory (leave blank for current dir):
|
|
107
|
+
|
|
108
|
+
✔ Created directory: /path/to/my_project
|
|
109
|
+
✔ Template extracted to: /path/to/my_project
|
|
110
|
+
✔ Placeholders replaced.
|
|
111
|
+
|
|
112
|
+
🎉 Done! Your new repo is ready at:
|
|
113
|
+
/path/to/my_project
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### Contributors
|
|
119
|
+
|
|
120
|
+
Setting up and maintaining biscuitcutter has been possible thanks to users and contributors. Thanks to:
|
|
121
|
+
|
|
122
|
+
<p align="left">
|
|
123
|
+
<a href="https://github.com/erdogant/biscuitcutter/graphs/contributors">
|
|
124
|
+
<img src="https://contrib.rocks/image?repo=erdogant/biscuitcutter" />
|
|
125
|
+
</a>
|
|
126
|
+
</p>
|
|
127
|
+
|
|
128
|
+
### Maintainer
|
|
129
|
+
* Erdogan Taskesen, github: [erdogant](https://github.com/erdogant)
|
|
130
|
+
* Contributions are welcome.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
biscuitcutter/__init__.py
|
|
6
|
+
biscuitcutter/biscuitcutter.py
|
|
7
|
+
biscuitcutter/examples.py
|
|
8
|
+
biscuitcutter/template.zip
|
|
9
|
+
biscuitcutter.egg-info/PKG-INFO
|
|
10
|
+
biscuitcutter.egg-info/SOURCES.txt
|
|
11
|
+
biscuitcutter.egg-info/dependency_links.txt
|
|
12
|
+
biscuitcutter.egg-info/entry_points.txt
|
|
13
|
+
biscuitcutter.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
biscuitcutter
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "biscuitcutter"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
authors = [{ name = "Erdogan Taskesen", email = "erdogant@gmail.com" }]
|
|
9
|
+
description = "biscuitcutter – scaffold a new Python repo from a template in seconds."
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
keywords = ["Python", "biscuitcutter", "templates", "scaffolding", "cookiecutter"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Education",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Operating System :: Unix",
|
|
20
|
+
"Operating System :: Microsoft :: Windows",
|
|
21
|
+
"Operating System :: MacOS",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
]
|
|
24
|
+
dependencies = []
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://erdogant.github.io/biscuitcutter"
|
|
28
|
+
Download = "https://github.com/erdogant/biscuitcutter/archive/{version}.tar.gz"
|
|
29
|
+
|
|
30
|
+
# ── CLI entry-points ───────────────────────────────────────────────────────
|
|
31
|
+
# After `pip install biscuitcutter` the user can type any of these commands
|
|
32
|
+
# in a terminal to launch the interactive scaffolder.
|
|
33
|
+
[project.scripts]
|
|
34
|
+
biscuitcutter = "biscuitcutter.biscuitcutter:main"
|
|
35
|
+
bakebiscuit = "biscuitcutter.biscuitcutter:main"
|
|
36
|
+
bakebiscuits = "biscuitcutter.biscuitcutter:main"
|
|
37
|
+
|
|
38
|
+
[tool.setuptools]
|
|
39
|
+
packages = ["biscuitcutter"]
|
|
40
|
+
include-package-data = true
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.package-data]
|
|
43
|
+
biscuitcutter = ["template.zip"]
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.dynamic]
|
|
46
|
+
version = { attr = "biscuitcutter.__version__" }
|