python-snacks 0.1.2__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.
- python_snacks-0.1.2/LICENSE +21 -0
- python_snacks-0.1.2/PKG-INFO +230 -0
- python_snacks-0.1.2/README.md +218 -0
- python_snacks-0.1.2/pyproject.toml +23 -0
- python_snacks-0.1.2/python_snacks.egg-info/PKG-INFO +230 -0
- python_snacks-0.1.2/python_snacks.egg-info/SOURCES.txt +15 -0
- python_snacks-0.1.2/python_snacks.egg-info/dependency_links.txt +1 -0
- python_snacks-0.1.2/python_snacks.egg-info/entry_points.txt +2 -0
- python_snacks-0.1.2/python_snacks.egg-info/requires.txt +4 -0
- python_snacks-0.1.2/python_snacks.egg-info/top_level.txt +1 -0
- python_snacks-0.1.2/setup.cfg +4 -0
- python_snacks-0.1.2/snacks/__init__.py +0 -0
- python_snacks-0.1.2/snacks/config.py +149 -0
- python_snacks-0.1.2/snacks/main.py +188 -0
- python_snacks-0.1.2/snacks/ops.py +126 -0
- python_snacks-0.1.2/tests/test_commands.py +161 -0
- python_snacks-0.1.2/tests/test_stash_commands.py +251 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kicka5h
|
|
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,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-snacks
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A CLI tool for managing a personal stash of reusable Python code snippets.
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: typer[all]>=0.9
|
|
9
|
+
Provides-Extra: test
|
|
10
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# Snack Stash
|
|
14
|
+
|
|
15
|
+
A personal CLI tool for managing a local stash of reusable Python code snippets. Browse, copy, and curate snippets across projects with a single command.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pipx install snack-stash
|
|
19
|
+
snack stash create default ~/snack-stash
|
|
20
|
+
snack unpack auth/google_oauth.py
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
**Recommended (pipx):**
|
|
28
|
+
```bash
|
|
29
|
+
pipx install snack-stash
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Alternative (pip):**
|
|
33
|
+
```bash
|
|
34
|
+
pip install snack-stash
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Requires Python 3.10+.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
### First-time setup
|
|
44
|
+
|
|
45
|
+
Create a stash directory and register it:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
snack stash create default ~/snack-stash
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This creates `~/snack-stash`, writes `~/.snackstashrc`, and sets `default` as the active stash.
|
|
52
|
+
|
|
53
|
+
### Environment variable (overrides config file)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
export SNACK_STASH=~/snack-stash
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Add to `~/.zshrc` or `~/.bashrc` to make it permanent. Takes priority over everything else.
|
|
60
|
+
|
|
61
|
+
### Config file (`~/.snackstashrc`)
|
|
62
|
+
|
|
63
|
+
Created automatically by `snack stash create`. You can also edit it by hand:
|
|
64
|
+
|
|
65
|
+
```ini
|
|
66
|
+
[config]
|
|
67
|
+
active = default
|
|
68
|
+
|
|
69
|
+
[stash.default]
|
|
70
|
+
path = ~/snack-stash
|
|
71
|
+
|
|
72
|
+
[stash.work]
|
|
73
|
+
path = ~/work-stash
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Priority order:** `SNACK_STASH` env var → `~/.snackstashrc` active stash → error.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Stash structure
|
|
81
|
+
|
|
82
|
+
A stash is a plain directory of `.py` files, organized into subdirectories by category:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
~/snack-stash/
|
|
86
|
+
├── auth/
|
|
87
|
+
│ ├── google_oauth_fastapi.py
|
|
88
|
+
│ ├── google_oauth_flask.py
|
|
89
|
+
│ └── jwt_helpers.py
|
|
90
|
+
├── forms/
|
|
91
|
+
│ ├── contact_form.py
|
|
92
|
+
│ └── newsletter_signup.py
|
|
93
|
+
└── email/
|
|
94
|
+
└── smtp_sender.py
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
You can manage the directory with Git, Dropbox, or any sync tool independently of this CLI.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Commands
|
|
102
|
+
|
|
103
|
+
### `snack list [category]`
|
|
104
|
+
|
|
105
|
+
List all snippets in the active stash.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
snack list # all snippets
|
|
109
|
+
snack list auth # filtered by category (subdirectory)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `snack search <keyword>`
|
|
113
|
+
|
|
114
|
+
Search snippet filenames for a keyword (case-insensitive).
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
snack search oauth
|
|
118
|
+
# auth/google_oauth_fastapi.py
|
|
119
|
+
# auth/google_oauth_flask.py
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `snack unpack <snippet>`
|
|
123
|
+
|
|
124
|
+
Copy a snippet **from the stash** into the current working directory.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
snack unpack auth/google_oauth_fastapi.py
|
|
128
|
+
# → ./auth/google_oauth_fastapi.py
|
|
129
|
+
|
|
130
|
+
snack unpack auth/google_oauth_fastapi.py --flat
|
|
131
|
+
# → ./google_oauth_fastapi.py (no subdirectory)
|
|
132
|
+
|
|
133
|
+
snack unpack auth/google_oauth_fastapi.py --force
|
|
134
|
+
# Overwrites without prompting
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### `snack pack <snippet>`
|
|
138
|
+
|
|
139
|
+
Copy a file **from the current directory** back into the stash. Use this when you've improved a snippet on a project and want to update the canonical version.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
snack pack auth/google_oauth_fastapi.py
|
|
143
|
+
snack pack auth/google_oauth_fastapi.py --force
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Stash management
|
|
149
|
+
|
|
150
|
+
### `snack stash create <name> <path>`
|
|
151
|
+
|
|
152
|
+
Register a new named stash. Creates the directory if it doesn't exist.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
snack stash create default ~/snack-stash
|
|
156
|
+
snack stash create work ~/work-stash --no-activate
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The first stash created is automatically set as active. Use `--no-activate` to add a stash without switching to it.
|
|
160
|
+
|
|
161
|
+
### `snack stash list`
|
|
162
|
+
|
|
163
|
+
Show all configured stashes and which one is active.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
snack stash list
|
|
167
|
+
# default /Users/you/snack-stash ← active
|
|
168
|
+
# work /Users/you/work-stash
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### `snack stash move <name> <new-path>`
|
|
172
|
+
|
|
173
|
+
Move a stash to a new location. Moves the directory on disk and updates the config.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
snack stash move default ~/new-location/snack-stash
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### `snack stash add-remote <repo>`
|
|
180
|
+
|
|
181
|
+
Copy Python snippets from a public GitHub repository into the active stash. Downloads the repo as a tarball and copies all `.py` files, preserving directory structure.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
snack stash add-remote owner/repo
|
|
185
|
+
snack stash add-remote https://github.com/owner/repo
|
|
186
|
+
snack stash add-remote owner/repo --subdir auth # only copy files under auth/
|
|
187
|
+
snack stash add-remote owner/repo --force # overwrite without prompting
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Error handling
|
|
193
|
+
|
|
194
|
+
| Situation | Behaviour |
|
|
195
|
+
|---|---|
|
|
196
|
+
| No stash configured | Error with setup instructions |
|
|
197
|
+
| Stash path doesn't exist | Error with the attempted path |
|
|
198
|
+
| Snippet not found in stash | Error — use `snack list` or `snack search` |
|
|
199
|
+
| Source file not found | Error |
|
|
200
|
+
| Destination file already exists | Prompt to confirm, or skip with `--force` |
|
|
201
|
+
| Named stash already exists | Error |
|
|
202
|
+
| Move target already exists | Error |
|
|
203
|
+
| GitHub repo not found (HTTP 404) | Error with status code |
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Project structure
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
python-snacks/
|
|
211
|
+
├── pyproject.toml
|
|
212
|
+
├── snacks/
|
|
213
|
+
│ ├── main.py # Typer app, all command definitions
|
|
214
|
+
│ ├── config.py # SnackConfig class, stash path resolution
|
|
215
|
+
│ └── ops.py # File copy logic (pack, unpack, add_remote)
|
|
216
|
+
└── tests/
|
|
217
|
+
├── test_commands.py # snippet commands
|
|
218
|
+
└── test_stash_commands.py # stash management commands
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## CI / CD
|
|
224
|
+
|
|
225
|
+
- **CI:** Tests run on every push and PR to `main` across Python 3.10, 3.11, and 3.12.
|
|
226
|
+
- **Publish:** Push a `v*` tag to trigger a build, PyPI publish, and GitHub Release.
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
git tag v0.2.0 && git push origin v0.2.0
|
|
230
|
+
```
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Snack Stash
|
|
2
|
+
|
|
3
|
+
A personal CLI tool for managing a local stash of reusable Python code snippets. Browse, copy, and curate snippets across projects with a single command.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pipx install snack-stash
|
|
7
|
+
snack stash create default ~/snack-stash
|
|
8
|
+
snack unpack auth/google_oauth.py
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
**Recommended (pipx):**
|
|
16
|
+
```bash
|
|
17
|
+
pipx install snack-stash
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Alternative (pip):**
|
|
21
|
+
```bash
|
|
22
|
+
pip install snack-stash
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Requires Python 3.10+.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
### First-time setup
|
|
32
|
+
|
|
33
|
+
Create a stash directory and register it:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
snack stash create default ~/snack-stash
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This creates `~/snack-stash`, writes `~/.snackstashrc`, and sets `default` as the active stash.
|
|
40
|
+
|
|
41
|
+
### Environment variable (overrides config file)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
export SNACK_STASH=~/snack-stash
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Add to `~/.zshrc` or `~/.bashrc` to make it permanent. Takes priority over everything else.
|
|
48
|
+
|
|
49
|
+
### Config file (`~/.snackstashrc`)
|
|
50
|
+
|
|
51
|
+
Created automatically by `snack stash create`. You can also edit it by hand:
|
|
52
|
+
|
|
53
|
+
```ini
|
|
54
|
+
[config]
|
|
55
|
+
active = default
|
|
56
|
+
|
|
57
|
+
[stash.default]
|
|
58
|
+
path = ~/snack-stash
|
|
59
|
+
|
|
60
|
+
[stash.work]
|
|
61
|
+
path = ~/work-stash
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Priority order:** `SNACK_STASH` env var → `~/.snackstashrc` active stash → error.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Stash structure
|
|
69
|
+
|
|
70
|
+
A stash is a plain directory of `.py` files, organized into subdirectories by category:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
~/snack-stash/
|
|
74
|
+
├── auth/
|
|
75
|
+
│ ├── google_oauth_fastapi.py
|
|
76
|
+
│ ├── google_oauth_flask.py
|
|
77
|
+
│ └── jwt_helpers.py
|
|
78
|
+
├── forms/
|
|
79
|
+
│ ├── contact_form.py
|
|
80
|
+
│ └── newsletter_signup.py
|
|
81
|
+
└── email/
|
|
82
|
+
└── smtp_sender.py
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
You can manage the directory with Git, Dropbox, or any sync tool independently of this CLI.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Commands
|
|
90
|
+
|
|
91
|
+
### `snack list [category]`
|
|
92
|
+
|
|
93
|
+
List all snippets in the active stash.
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
snack list # all snippets
|
|
97
|
+
snack list auth # filtered by category (subdirectory)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `snack search <keyword>`
|
|
101
|
+
|
|
102
|
+
Search snippet filenames for a keyword (case-insensitive).
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
snack search oauth
|
|
106
|
+
# auth/google_oauth_fastapi.py
|
|
107
|
+
# auth/google_oauth_flask.py
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `snack unpack <snippet>`
|
|
111
|
+
|
|
112
|
+
Copy a snippet **from the stash** into the current working directory.
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
snack unpack auth/google_oauth_fastapi.py
|
|
116
|
+
# → ./auth/google_oauth_fastapi.py
|
|
117
|
+
|
|
118
|
+
snack unpack auth/google_oauth_fastapi.py --flat
|
|
119
|
+
# → ./google_oauth_fastapi.py (no subdirectory)
|
|
120
|
+
|
|
121
|
+
snack unpack auth/google_oauth_fastapi.py --force
|
|
122
|
+
# Overwrites without prompting
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `snack pack <snippet>`
|
|
126
|
+
|
|
127
|
+
Copy a file **from the current directory** back into the stash. Use this when you've improved a snippet on a project and want to update the canonical version.
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
snack pack auth/google_oauth_fastapi.py
|
|
131
|
+
snack pack auth/google_oauth_fastapi.py --force
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Stash management
|
|
137
|
+
|
|
138
|
+
### `snack stash create <name> <path>`
|
|
139
|
+
|
|
140
|
+
Register a new named stash. Creates the directory if it doesn't exist.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
snack stash create default ~/snack-stash
|
|
144
|
+
snack stash create work ~/work-stash --no-activate
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The first stash created is automatically set as active. Use `--no-activate` to add a stash without switching to it.
|
|
148
|
+
|
|
149
|
+
### `snack stash list`
|
|
150
|
+
|
|
151
|
+
Show all configured stashes and which one is active.
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
snack stash list
|
|
155
|
+
# default /Users/you/snack-stash ← active
|
|
156
|
+
# work /Users/you/work-stash
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### `snack stash move <name> <new-path>`
|
|
160
|
+
|
|
161
|
+
Move a stash to a new location. Moves the directory on disk and updates the config.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
snack stash move default ~/new-location/snack-stash
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### `snack stash add-remote <repo>`
|
|
168
|
+
|
|
169
|
+
Copy Python snippets from a public GitHub repository into the active stash. Downloads the repo as a tarball and copies all `.py` files, preserving directory structure.
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
snack stash add-remote owner/repo
|
|
173
|
+
snack stash add-remote https://github.com/owner/repo
|
|
174
|
+
snack stash add-remote owner/repo --subdir auth # only copy files under auth/
|
|
175
|
+
snack stash add-remote owner/repo --force # overwrite without prompting
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Error handling
|
|
181
|
+
|
|
182
|
+
| Situation | Behaviour |
|
|
183
|
+
|---|---|
|
|
184
|
+
| No stash configured | Error with setup instructions |
|
|
185
|
+
| Stash path doesn't exist | Error with the attempted path |
|
|
186
|
+
| Snippet not found in stash | Error — use `snack list` or `snack search` |
|
|
187
|
+
| Source file not found | Error |
|
|
188
|
+
| Destination file already exists | Prompt to confirm, or skip with `--force` |
|
|
189
|
+
| Named stash already exists | Error |
|
|
190
|
+
| Move target already exists | Error |
|
|
191
|
+
| GitHub repo not found (HTTP 404) | Error with status code |
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Project structure
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
python-snacks/
|
|
199
|
+
├── pyproject.toml
|
|
200
|
+
├── snacks/
|
|
201
|
+
│ ├── main.py # Typer app, all command definitions
|
|
202
|
+
│ ├── config.py # SnackConfig class, stash path resolution
|
|
203
|
+
│ └── ops.py # File copy logic (pack, unpack, add_remote)
|
|
204
|
+
└── tests/
|
|
205
|
+
├── test_commands.py # snippet commands
|
|
206
|
+
└── test_stash_commands.py # stash management commands
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## CI / CD
|
|
212
|
+
|
|
213
|
+
- **CI:** Tests run on every push and PR to `main` across Python 3.10, 3.11, and 3.12.
|
|
214
|
+
- **Publish:** Push a `v*` tag to trigger a build, PyPI publish, and GitHub Release.
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
git tag v0.2.0 && git push origin v0.2.0
|
|
218
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "python-snacks"
|
|
7
|
+
version = "0.1.2"
|
|
8
|
+
description = "A CLI tool for managing a personal stash of reusable Python code snippets."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"typer[all]>=0.9",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[project.scripts]
|
|
16
|
+
snack = "snacks.main:app"
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
test = ["pytest>=8"]
|
|
20
|
+
|
|
21
|
+
[tool.setuptools.packages.find]
|
|
22
|
+
where = ["."]
|
|
23
|
+
include = ["snacks*"]
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-snacks
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A CLI tool for managing a personal stash of reusable Python code snippets.
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: typer[all]>=0.9
|
|
9
|
+
Provides-Extra: test
|
|
10
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
11
|
+
Dynamic: license-file
|
|
12
|
+
|
|
13
|
+
# Snack Stash
|
|
14
|
+
|
|
15
|
+
A personal CLI tool for managing a local stash of reusable Python code snippets. Browse, copy, and curate snippets across projects with a single command.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pipx install snack-stash
|
|
19
|
+
snack stash create default ~/snack-stash
|
|
20
|
+
snack unpack auth/google_oauth.py
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
**Recommended (pipx):**
|
|
28
|
+
```bash
|
|
29
|
+
pipx install snack-stash
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Alternative (pip):**
|
|
33
|
+
```bash
|
|
34
|
+
pip install snack-stash
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Requires Python 3.10+.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
### First-time setup
|
|
44
|
+
|
|
45
|
+
Create a stash directory and register it:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
snack stash create default ~/snack-stash
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This creates `~/snack-stash`, writes `~/.snackstashrc`, and sets `default` as the active stash.
|
|
52
|
+
|
|
53
|
+
### Environment variable (overrides config file)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
export SNACK_STASH=~/snack-stash
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Add to `~/.zshrc` or `~/.bashrc` to make it permanent. Takes priority over everything else.
|
|
60
|
+
|
|
61
|
+
### Config file (`~/.snackstashrc`)
|
|
62
|
+
|
|
63
|
+
Created automatically by `snack stash create`. You can also edit it by hand:
|
|
64
|
+
|
|
65
|
+
```ini
|
|
66
|
+
[config]
|
|
67
|
+
active = default
|
|
68
|
+
|
|
69
|
+
[stash.default]
|
|
70
|
+
path = ~/snack-stash
|
|
71
|
+
|
|
72
|
+
[stash.work]
|
|
73
|
+
path = ~/work-stash
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Priority order:** `SNACK_STASH` env var → `~/.snackstashrc` active stash → error.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Stash structure
|
|
81
|
+
|
|
82
|
+
A stash is a plain directory of `.py` files, organized into subdirectories by category:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
~/snack-stash/
|
|
86
|
+
├── auth/
|
|
87
|
+
│ ├── google_oauth_fastapi.py
|
|
88
|
+
│ ├── google_oauth_flask.py
|
|
89
|
+
│ └── jwt_helpers.py
|
|
90
|
+
├── forms/
|
|
91
|
+
│ ├── contact_form.py
|
|
92
|
+
│ └── newsletter_signup.py
|
|
93
|
+
└── email/
|
|
94
|
+
└── smtp_sender.py
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
You can manage the directory with Git, Dropbox, or any sync tool independently of this CLI.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Commands
|
|
102
|
+
|
|
103
|
+
### `snack list [category]`
|
|
104
|
+
|
|
105
|
+
List all snippets in the active stash.
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
snack list # all snippets
|
|
109
|
+
snack list auth # filtered by category (subdirectory)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `snack search <keyword>`
|
|
113
|
+
|
|
114
|
+
Search snippet filenames for a keyword (case-insensitive).
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
snack search oauth
|
|
118
|
+
# auth/google_oauth_fastapi.py
|
|
119
|
+
# auth/google_oauth_flask.py
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `snack unpack <snippet>`
|
|
123
|
+
|
|
124
|
+
Copy a snippet **from the stash** into the current working directory.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
snack unpack auth/google_oauth_fastapi.py
|
|
128
|
+
# → ./auth/google_oauth_fastapi.py
|
|
129
|
+
|
|
130
|
+
snack unpack auth/google_oauth_fastapi.py --flat
|
|
131
|
+
# → ./google_oauth_fastapi.py (no subdirectory)
|
|
132
|
+
|
|
133
|
+
snack unpack auth/google_oauth_fastapi.py --force
|
|
134
|
+
# Overwrites without prompting
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### `snack pack <snippet>`
|
|
138
|
+
|
|
139
|
+
Copy a file **from the current directory** back into the stash. Use this when you've improved a snippet on a project and want to update the canonical version.
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
snack pack auth/google_oauth_fastapi.py
|
|
143
|
+
snack pack auth/google_oauth_fastapi.py --force
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Stash management
|
|
149
|
+
|
|
150
|
+
### `snack stash create <name> <path>`
|
|
151
|
+
|
|
152
|
+
Register a new named stash. Creates the directory if it doesn't exist.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
snack stash create default ~/snack-stash
|
|
156
|
+
snack stash create work ~/work-stash --no-activate
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The first stash created is automatically set as active. Use `--no-activate` to add a stash without switching to it.
|
|
160
|
+
|
|
161
|
+
### `snack stash list`
|
|
162
|
+
|
|
163
|
+
Show all configured stashes and which one is active.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
snack stash list
|
|
167
|
+
# default /Users/you/snack-stash ← active
|
|
168
|
+
# work /Users/you/work-stash
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### `snack stash move <name> <new-path>`
|
|
172
|
+
|
|
173
|
+
Move a stash to a new location. Moves the directory on disk and updates the config.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
snack stash move default ~/new-location/snack-stash
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### `snack stash add-remote <repo>`
|
|
180
|
+
|
|
181
|
+
Copy Python snippets from a public GitHub repository into the active stash. Downloads the repo as a tarball and copies all `.py` files, preserving directory structure.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
snack stash add-remote owner/repo
|
|
185
|
+
snack stash add-remote https://github.com/owner/repo
|
|
186
|
+
snack stash add-remote owner/repo --subdir auth # only copy files under auth/
|
|
187
|
+
snack stash add-remote owner/repo --force # overwrite without prompting
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Error handling
|
|
193
|
+
|
|
194
|
+
| Situation | Behaviour |
|
|
195
|
+
|---|---|
|
|
196
|
+
| No stash configured | Error with setup instructions |
|
|
197
|
+
| Stash path doesn't exist | Error with the attempted path |
|
|
198
|
+
| Snippet not found in stash | Error — use `snack list` or `snack search` |
|
|
199
|
+
| Source file not found | Error |
|
|
200
|
+
| Destination file already exists | Prompt to confirm, or skip with `--force` |
|
|
201
|
+
| Named stash already exists | Error |
|
|
202
|
+
| Move target already exists | Error |
|
|
203
|
+
| GitHub repo not found (HTTP 404) | Error with status code |
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Project structure
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
python-snacks/
|
|
211
|
+
├── pyproject.toml
|
|
212
|
+
├── snacks/
|
|
213
|
+
│ ├── main.py # Typer app, all command definitions
|
|
214
|
+
│ ├── config.py # SnackConfig class, stash path resolution
|
|
215
|
+
│ └── ops.py # File copy logic (pack, unpack, add_remote)
|
|
216
|
+
└── tests/
|
|
217
|
+
├── test_commands.py # snippet commands
|
|
218
|
+
└── test_stash_commands.py # stash management commands
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## CI / CD
|
|
224
|
+
|
|
225
|
+
- **CI:** Tests run on every push and PR to `main` across Python 3.10, 3.11, and 3.12.
|
|
226
|
+
- **Publish:** Push a `v*` tag to trigger a build, PyPI publish, and GitHub Release.
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
git tag v0.2.0 && git push origin v0.2.0
|
|
230
|
+
```
|