python-package-folder 1.0.0__tar.gz → 1.1.1__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_package_folder-1.0.0 → python_package_folder-1.1.1}/.vscode/settings.json +1 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/PKG-INFO +215 -130
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/README.md +214 -129
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/coverage.svg +2 -2
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/finder.py +39 -10
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/manager.py +57 -5
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/python_package_folder.py +7 -1
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/.copier-answers.yml +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/.cursor/rules/general.mdc +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/.cursor/rules/python.mdc +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/.github/workflows/ci.yml +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/.github/workflows/publish.yml +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/.gitignore +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/LICENSE +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/Makefile +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/development.md +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/installation.md +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/publishing.md +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/pyproject.toml +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/__init__.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/__main__.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/analyzer.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/publisher.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/py.typed +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/subfolder_build.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/types.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/utils.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/src/python_package_folder/version.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/folder_structure/some_globals.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/folder_structure/subfolder_to_build/README.md +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/folder_structure/subfolder_to_build/some_function.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/folder_structure/utility_folder/some_utility.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/test_build_with_external_deps.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/test_publisher.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/test_subfolder_build.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/test_utils.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/test_version_manager.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/tests/tests.py +0 -0
- {python_package_folder-1.0.0 → python_package_folder-1.1.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-package-folder
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Python package to automatically package and build a folder, fetching all relevant dependencies.
|
|
5
5
|
Project-URL: Repository, https://github.com/alelom/python-package-folder
|
|
6
6
|
Author-email: Alessio Lombardi <work@alelom.com>
|
|
@@ -18,42 +18,143 @@ Classifier: Typing :: Typed
|
|
|
18
18
|
Requires-Python: <4.0,>=3.11
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
|
|
21
|
-
# python-package-folder
|
|
21
|
+
# python-package-folder <!-- omit from toc -->
|
|
22
22
|
|
|
23
23
|
[](https://github.com/alelom/python-package-folder/actions/workflows/ci.yml)
|
|
24
24
|
[](https://github.com/alelom/python-package-folder)
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Easily build and publish any target folder in a repository, including subfolders of a monorepo.
|
|
27
|
+
Together with [sysappend](https://pypi.org/project/sysappend/), this library makes relative imports, flexible import management, and package publishing a breeze.
|
|
28
|
+
|
|
29
|
+
- [Use Cases](#use-cases)
|
|
30
|
+
- [Features](#features)
|
|
31
|
+
- [Installation and requirements](#installation-and-requirements)
|
|
32
|
+
- [Quick Start](#quick-start)
|
|
33
|
+
- [How does `python-package-folder` work?](#how-does-python-package-folder-work)
|
|
34
|
+
- [Python API Usage](#python-api-usage)
|
|
35
|
+
- [Working with sysappend](#working-with-sysappend)
|
|
36
|
+
- [Publishing version Management](#publishing-version-management)
|
|
37
|
+
- [Publishing Packages](#publishing-packages)
|
|
38
|
+
- [Command Line Options](#command-line-options)
|
|
39
|
+
- [API Reference](#api-reference)
|
|
40
|
+
- [Development](#development)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Use Cases
|
|
44
|
+
|
|
45
|
+
### 1) Publishing a Subfolder from src/ in a Monorepo
|
|
46
|
+
|
|
47
|
+
If you have a monorepo structure with multiple packages in `src/`:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
project/
|
|
51
|
+
├── src/
|
|
52
|
+
│ ├── core_package/
|
|
53
|
+
│ │ ├── __init__.py
|
|
54
|
+
│ │ ├── core.py
|
|
55
|
+
│ │ └── README.md
|
|
56
|
+
│ ├── api_package/
|
|
57
|
+
│ │ ├── __init__.py
|
|
58
|
+
│ │ ├── api.py
|
|
59
|
+
│ │ └── README.md
|
|
60
|
+
│ └── utils_package/
|
|
61
|
+
│ ├── __init__.py
|
|
62
|
+
│ ├── utils.py
|
|
63
|
+
│ └── README.md
|
|
64
|
+
├── shared/
|
|
65
|
+
│ └── common.py
|
|
66
|
+
└── pyproject.toml
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
You can build and publish any subfolder from `src/` as a standalone package:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Navigate to the subfolder you want to publish
|
|
73
|
+
cd src/api_package
|
|
74
|
+
|
|
75
|
+
# Build and publish to TestPyPI with version 1.2.0
|
|
76
|
+
python-package-folder --publish testpypi --version 1.2.0
|
|
77
|
+
|
|
78
|
+
# Or publish to PyPI with a custom package name
|
|
79
|
+
python-package-folder --publish pypi --version 1.2.0 --package-name "my-api-package"
|
|
80
|
+
|
|
81
|
+
# Include a specific dependency group from the parent pyproject.toml
|
|
82
|
+
python-package-folder --publish pypi --version 1.2.0 --dependency-group "dev"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The tool will automatically:
|
|
86
|
+
1. Detect the project root (where `pyproject.toml` is located)
|
|
87
|
+
2. Use `src/api_package` as the source directory
|
|
88
|
+
3. Copy any external dependencies (like `shared/common.py`) into the package before building
|
|
89
|
+
4. Use the subfolder's README if present, or create a minimal one
|
|
90
|
+
5. Create a temporary `pyproject.toml` with the subfolder's package name and version
|
|
91
|
+
6. Build and publish the package
|
|
92
|
+
7. Clean up all temporary files and restore the original `pyproject.toml`
|
|
93
|
+
|
|
94
|
+
This is especially useful for monorepos where you want to publish individual packages independently while sharing common code.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
### 2) Building Packages with Shared Code
|
|
98
|
+
|
|
99
|
+
If your project structure looks like this:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
project/
|
|
103
|
+
├── src/
|
|
104
|
+
│ └── my_package/
|
|
105
|
+
│ └── main.py
|
|
106
|
+
├── shared/
|
|
107
|
+
│ ├── utils.py
|
|
108
|
+
│ └── helpers.py
|
|
109
|
+
└── pyproject.toml
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
And `main.py` imports from `shared/`:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from shared.utils import some_function
|
|
116
|
+
from shared.helpers import Helper
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This package will automatically:
|
|
120
|
+
1. Detect that `shared/` is outside `src/`
|
|
121
|
+
2. Copy `shared/` into `src/` before building
|
|
122
|
+
3. Build your package with all dependencies included
|
|
123
|
+
4. Clean up the copied files after build
|
|
124
|
+
|
|
27
125
|
|
|
28
126
|
## Features
|
|
29
127
|
|
|
30
|
-
- **
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
|
|
128
|
+
- **Subfolder Build Support**: Build subfolders as separate packages with automatic project root detection
|
|
129
|
+
- Creates any needed file for publishing automatically, cleaning up if not originally in the subfolder after the build/publish process. E.g. copies external dependencies into the source directory before build and cleans them up afterward; temporary `__init__.py` creation for non-package subfolders; uses subfolder README if present, otherwise creates minimal README
|
|
130
|
+
- Automatic package name derivation from subfolder name
|
|
131
|
+
- Dependency group selection: specify which dependency group from parent `pyproject.toml` to include.
|
|
132
|
+
|
|
133
|
+
- **Smart Import Classification and analysis**:
|
|
134
|
+
- Recursively parses all `.py` files to detect `import` and `from ... import ...` statements
|
|
135
|
+
- Handles external dependencies (modules and files that originate from outside the main package directory), and distinguishes standard library imports, 3rd-party packages (from site-packages), local/external/relative/ambiguous imports.
|
|
136
|
+
|
|
39
137
|
- **Idempotent Operations**: Safely handles repeated runs without duplicating files
|
|
40
138
|
- **Build Integration**: Seamlessly integrates with build tools like `uv build`, `pip build`, etc.
|
|
41
|
-
- **
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
- **
|
|
139
|
+
- **Version Management**:
|
|
140
|
+
- Set static versions for publishing (PEP 440 compliant)
|
|
141
|
+
- Temporarily override dynamic versioning during builds
|
|
142
|
+
- Automatic restoration of dynamic versioning after build
|
|
143
|
+
- **Package Publishing**:
|
|
144
|
+
- Uses twine to publish the built folder/subfolder
|
|
145
|
+
- Handles publishing to to PyPI, TestPyPI, or Azure Artifacts, with interactive credential prompts, secure storage support
|
|
46
146
|
|
|
47
|
-
## Installation
|
|
48
147
|
|
|
49
|
-
|
|
50
|
-
pip install python-package-folder
|
|
51
|
-
```
|
|
148
|
+
## Installation and requirements
|
|
52
149
|
|
|
53
|
-
|
|
150
|
+
Python >= 3.11 is required.
|
|
54
151
|
|
|
55
152
|
```bash
|
|
56
153
|
uv add python-package-folder
|
|
154
|
+
|
|
155
|
+
# or
|
|
156
|
+
|
|
157
|
+
pip install python-package-folder
|
|
57
158
|
```
|
|
58
159
|
|
|
59
160
|
**Note**: For publishing functionality, you'll also need `twine`:
|
|
@@ -64,30 +165,97 @@ pip install twine
|
|
|
64
165
|
uv add twine
|
|
65
166
|
```
|
|
66
167
|
|
|
168
|
+
**For secure credential storage**: `keyring` is optional but recommended (install with `pip install keyring`)
|
|
169
|
+
|
|
170
|
+
|
|
67
171
|
## Quick Start
|
|
68
172
|
|
|
69
|
-
|
|
173
|
+
The simplest way to use this package is via the command-line interface
|
|
70
174
|
|
|
71
|
-
|
|
175
|
+
**Build/publish a specific subfolder in a repository**
|
|
176
|
+
|
|
177
|
+
Useful for monorepos containing many subfolders that may need publishing as stand-alone packages for external usage.
|
|
72
178
|
|
|
73
179
|
```bash
|
|
74
|
-
#
|
|
75
|
-
|
|
180
|
+
# First cd to the specific subfolder
|
|
181
|
+
cd src/subfolder_to_build_and_publish
|
|
76
182
|
|
|
77
|
-
#
|
|
78
|
-
python-package-folder --
|
|
183
|
+
# Build and publish any subdirectory of your repo to TestPyPi (https://test.pypi.org/)
|
|
184
|
+
python-package-folder --publish testpypi --version 0.0.2
|
|
79
185
|
|
|
80
|
-
#
|
|
81
|
-
cd
|
|
186
|
+
# Only analyse (no building)
|
|
187
|
+
cd src/subfolder_to_build_and_publish
|
|
82
188
|
python-package-folder --analyze-only
|
|
83
189
|
|
|
190
|
+
# Only build
|
|
191
|
+
cd src/subfolder_to_build_and_publish
|
|
192
|
+
python-package-folder
|
|
193
|
+
|
|
194
|
+
# Build with automatic dependency management
|
|
195
|
+
python-package-folder --build-command "uv build"
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
You can also target a specific subfolder via commandline, rather than `cd`ing there:
|
|
199
|
+
|
|
200
|
+
```python
|
|
84
201
|
# Specify custom project root and source directory
|
|
85
202
|
python-package-folder --project-root /path/to/project --src-dir /path/to/src --build-command "pip build"
|
|
86
203
|
```
|
|
87
204
|
|
|
88
|
-
|
|
205
|
+
## How does `python-package-folder` work?
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
### Build Process
|
|
209
|
+
|
|
210
|
+
1. **Import Extraction**: Uses Python's AST module to parse all `.py` files and extract import statements
|
|
211
|
+
2. **Classification**: Each import is classified as:
|
|
212
|
+
- **stdlib**: Standard library modules
|
|
213
|
+
- **third_party**: Packages installed in site-packages
|
|
214
|
+
- **local**: Modules within the source directory
|
|
215
|
+
- **external**: Modules outside source directory but in the project
|
|
216
|
+
- **ambiguous**: Cannot be resolved
|
|
217
|
+
3. **Dependency Resolution**: For external imports, the tool resolves the file path by checking:
|
|
218
|
+
- Parent directories of the source directory
|
|
219
|
+
- Project root and its subdirectories
|
|
220
|
+
- Relative import paths
|
|
221
|
+
4. **File Copying**: External dependencies are temporarily copied into the source directory
|
|
222
|
+
5. **Build Execution**: Your build command runs with all dependencies in place
|
|
223
|
+
6. **Cleanup**: All temporarily copied files are removed after build
|
|
224
|
+
|
|
225
|
+
### Publishing Process
|
|
226
|
+
|
|
227
|
+
1. **Build Verification**: Ensures distribution files exist in the `dist/` directory
|
|
228
|
+
2. **File Filtering**: Automatically filters distribution files to only include those matching the current package name and version (prevents uploading old artifacts)
|
|
229
|
+
3. **Credential Management**:
|
|
230
|
+
- Prompts for credentials if not provided
|
|
231
|
+
- Uses `keyring` for secure storage (if available)
|
|
232
|
+
- Supports both username/password and API tokens
|
|
233
|
+
- Auto-detects API tokens and uses `__token__` as username
|
|
234
|
+
4. **Repository Configuration**: Configures the target repository (PyPI, TestPyPI, or Azure)
|
|
235
|
+
5. **Upload**: Uses `twine` to upload distribution files to the repository
|
|
236
|
+
6. **Verification**: Confirms successful upload
|
|
237
|
+
|
|
238
|
+
### Subfolder Build Process
|
|
89
239
|
|
|
90
|
-
|
|
240
|
+
1. **Project Root Detection**: Searches parent directories for `pyproject.toml`
|
|
241
|
+
2. **Source Directory Detection**: Uses current directory if it contains Python files, otherwise falls back to `project_root/src`
|
|
242
|
+
3. **Package Initialization**: Creates temporary `__init__.py` if subfolder doesn't have one (required for hatchling)
|
|
243
|
+
4. **README Handling**:
|
|
244
|
+
- Checks for README files in the subfolder (README.md, README.rst, README.txt, or README)
|
|
245
|
+
- If found, copies the subfolder README to project root (backing up the original parent README)
|
|
246
|
+
- If not found, creates a minimal README with just the folder name
|
|
247
|
+
5. **Configuration Creation**: Creates temporary `pyproject.toml` with:
|
|
248
|
+
- Subfolder-specific package name (derived or custom)
|
|
249
|
+
- Specified version
|
|
250
|
+
- Correct package path for hatchling
|
|
251
|
+
6. **Build Execution**: Runs build command with all dependencies in place
|
|
252
|
+
7. **Cleanup**: Restores original `pyproject.toml` and removes temporary `__init__.py`
|
|
253
|
+
|
|
254
|
+
### How does building from Subdirectories work?
|
|
255
|
+
|
|
256
|
+
This is useful for monorepos containing many subfolders that may need publishing as stand-alone packages for external usage.
|
|
257
|
+
The tool automatically detects the project root by searching for `pyproject.toml` in parent directories.
|
|
258
|
+
This allows you to build subfolders of a main project as separate packages:
|
|
91
259
|
|
|
92
260
|
```bash
|
|
93
261
|
# From a subdirectory, the tool will:
|
|
@@ -100,7 +268,7 @@ cd my_project/subfolder_to_build
|
|
|
100
268
|
python-package-folder --version "1.0.0" --publish pypi
|
|
101
269
|
```
|
|
102
270
|
|
|
103
|
-
|
|
271
|
+
When building from a subdirectory, you **must** specify `--version` because subfolders are built as separate packages with their own version.
|
|
104
272
|
|
|
105
273
|
The tool automatically:
|
|
106
274
|
- Finds the project root by looking for `pyproject.toml` in parent directories
|
|
@@ -136,7 +304,7 @@ python-package-folder --version "1.0.0" --dependency-group "dev" --publish pypi
|
|
|
136
304
|
|
|
137
305
|
The specified dependency group will be copied from the parent `pyproject.toml`'s `[dependency-groups]` section into the temporary `pyproject.toml` used for the subfolder build.
|
|
138
306
|
|
|
139
|
-
|
|
307
|
+
## Python API Usage
|
|
140
308
|
|
|
141
309
|
You can also use the package programmatically:
|
|
142
310
|
|
|
@@ -180,37 +348,7 @@ def build_command():
|
|
|
180
348
|
manager.run_build(build_command)
|
|
181
349
|
```
|
|
182
350
|
|
|
183
|
-
##
|
|
184
|
-
|
|
185
|
-
### Building Packages with Shared Code
|
|
186
|
-
|
|
187
|
-
If your project structure looks like this:
|
|
188
|
-
|
|
189
|
-
```
|
|
190
|
-
project/
|
|
191
|
-
├── src/
|
|
192
|
-
│ └── my_package/
|
|
193
|
-
│ └── main.py
|
|
194
|
-
├── shared/
|
|
195
|
-
│ ├── utils.py
|
|
196
|
-
│ └── helpers.py
|
|
197
|
-
└── pyproject.toml
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
And `main.py` imports from `shared/`:
|
|
201
|
-
|
|
202
|
-
```python
|
|
203
|
-
from shared.utils import some_function
|
|
204
|
-
from shared.helpers import Helper
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
This package will automatically:
|
|
208
|
-
1. Detect that `shared/` is outside `src/`
|
|
209
|
-
2. Copy `shared/` into `src/` before building
|
|
210
|
-
3. Build your package with all dependencies included
|
|
211
|
-
4. Clean up the copied files after build
|
|
212
|
-
|
|
213
|
-
### Working with sysappend
|
|
351
|
+
## Working with sysappend
|
|
214
352
|
|
|
215
353
|
This package works well with projects using [sysappend](https://pypi.org/project/sysappend/) for flexible import management. When you have imports like:
|
|
216
354
|
|
|
@@ -224,10 +362,11 @@ from folder_structure.utility_folder.some_utility import print_something
|
|
|
224
362
|
|
|
225
363
|
The package will correctly identify and copy external dependencies even when they're referenced without full package paths.
|
|
226
364
|
|
|
227
|
-
##
|
|
365
|
+
## Publishing version Management
|
|
228
366
|
|
|
229
367
|
The package supports both dynamic versioning (from git tags) and manual version specification.
|
|
230
368
|
|
|
369
|
+
|
|
231
370
|
### Manual Version Setting
|
|
232
371
|
|
|
233
372
|
You can manually set a version before building and publishing:
|
|
@@ -251,6 +390,7 @@ The `--version` option:
|
|
|
251
390
|
|
|
252
391
|
**Version Format**: Versions must follow PEP 440 (e.g., `1.2.3`, `1.2.3a1`, `1.2.3.post1`, `1.2.3.dev1`)
|
|
253
392
|
|
|
393
|
+
|
|
254
394
|
### Subfolder Versioning
|
|
255
395
|
|
|
256
396
|
When building from a subdirectory (not the main `src/` directory), you **must** specify `--version`:
|
|
@@ -278,6 +418,7 @@ For subfolder builds:
|
|
|
278
418
|
- If no README exists in the subfolder, a minimal README with just the folder name will be created
|
|
279
419
|
- **Auto-restore**: Original `pyproject.toml` is restored after build, and temporary `__init__.py` files are removed
|
|
280
420
|
|
|
421
|
+
|
|
281
422
|
### Python API for Version Management
|
|
282
423
|
|
|
283
424
|
```python
|
|
@@ -318,6 +459,7 @@ When you use `--version`, the package temporarily switches to static versioning
|
|
|
318
459
|
|
|
319
460
|
The package includes built-in support for publishing to PyPI, TestPyPI, and Azure Artifacts.
|
|
320
461
|
|
|
462
|
+
|
|
321
463
|
### Command Line Publishing
|
|
322
464
|
|
|
323
465
|
Publish after building:
|
|
@@ -346,6 +488,7 @@ python-package-folder --publish pypi --username __token__ --password pypi-xxxxx
|
|
|
346
488
|
python-package-folder --publish pypi --skip-existing
|
|
347
489
|
```
|
|
348
490
|
|
|
491
|
+
|
|
349
492
|
### Credentials
|
|
350
493
|
|
|
351
494
|
**For PyPI/TestPyPI:**
|
|
@@ -357,6 +500,7 @@ python-package-folder --publish pypi --skip-existing
|
|
|
357
500
|
- **403 Forbidden**: Usually means you used your username instead of `__token__` with an API token. The tool now auto-detects this.
|
|
358
501
|
- **TestPyPI vs PyPI**: TestPyPI requires a separate account and token from https://test.pypi.org/manage/account/token/
|
|
359
502
|
|
|
503
|
+
|
|
360
504
|
### Smart File Filtering
|
|
361
505
|
|
|
362
506
|
When publishing, the tool automatically filters distribution files to only upload those matching the current build:
|
|
@@ -377,6 +521,7 @@ To get a PyPI API token:
|
|
|
377
521
|
- **Password**: Personal Access Token (PAT) with packaging permissions
|
|
378
522
|
- **Repository URL**: Your Azure Artifacts feed URL
|
|
379
523
|
|
|
524
|
+
|
|
380
525
|
### Python API Publishing
|
|
381
526
|
|
|
382
527
|
You can also publish programmatically:
|
|
@@ -417,6 +562,7 @@ publisher = Publisher(
|
|
|
417
562
|
publisher.publish()
|
|
418
563
|
```
|
|
419
564
|
|
|
565
|
+
|
|
420
566
|
### Credential Storage
|
|
421
567
|
|
|
422
568
|
The package uses the `keyring` library (if installed) to securely store credentials. Credentials are stored per repository and will be reused on subsequent runs.
|
|
@@ -600,59 +746,6 @@ config.restore()
|
|
|
600
746
|
- If no README exists in the subfolder, a minimal README with just the folder name will be created
|
|
601
747
|
- The original parent README is backed up and restored after the build completes
|
|
602
748
|
|
|
603
|
-
## How It Works
|
|
604
|
-
|
|
605
|
-
### Build Process
|
|
606
|
-
|
|
607
|
-
1. **Import Extraction**: Uses Python's AST module to parse all `.py` files and extract import statements
|
|
608
|
-
2. **Classification**: Each import is classified as:
|
|
609
|
-
- **stdlib**: Standard library modules
|
|
610
|
-
- **third_party**: Packages installed in site-packages
|
|
611
|
-
- **local**: Modules within the source directory
|
|
612
|
-
- **external**: Modules outside source directory but in the project
|
|
613
|
-
- **ambiguous**: Cannot be resolved
|
|
614
|
-
3. **Dependency Resolution**: For external imports, the tool resolves the file path by checking:
|
|
615
|
-
- Parent directories of the source directory
|
|
616
|
-
- Project root and its subdirectories
|
|
617
|
-
- Relative import paths
|
|
618
|
-
4. **File Copying**: External dependencies are temporarily copied into the source directory
|
|
619
|
-
5. **Build Execution**: Your build command runs with all dependencies in place
|
|
620
|
-
6. **Cleanup**: All temporarily copied files are removed after build
|
|
621
|
-
|
|
622
|
-
### Publishing Process
|
|
623
|
-
|
|
624
|
-
1. **Build Verification**: Ensures distribution files exist in the `dist/` directory
|
|
625
|
-
2. **File Filtering**: Automatically filters distribution files to only include those matching the current package name and version (prevents uploading old artifacts)
|
|
626
|
-
3. **Credential Management**:
|
|
627
|
-
- Prompts for credentials if not provided
|
|
628
|
-
- Uses `keyring` for secure storage (if available)
|
|
629
|
-
- Supports both username/password and API tokens
|
|
630
|
-
- Auto-detects API tokens and uses `__token__` as username
|
|
631
|
-
4. **Repository Configuration**: Configures the target repository (PyPI, TestPyPI, or Azure)
|
|
632
|
-
5. **Upload**: Uses `twine` to upload distribution files to the repository
|
|
633
|
-
6. **Verification**: Confirms successful upload
|
|
634
|
-
|
|
635
|
-
### Subfolder Build Process
|
|
636
|
-
|
|
637
|
-
1. **Project Root Detection**: Searches parent directories for `pyproject.toml`
|
|
638
|
-
2. **Source Directory Detection**: Uses current directory if it contains Python files, otherwise falls back to `project_root/src`
|
|
639
|
-
3. **Package Initialization**: Creates temporary `__init__.py` if subfolder doesn't have one (required for hatchling)
|
|
640
|
-
4. **README Handling**:
|
|
641
|
-
- Checks for README files in the subfolder (README.md, README.rst, README.txt, or README)
|
|
642
|
-
- If found, copies the subfolder README to project root (backing up the original parent README)
|
|
643
|
-
- If not found, creates a minimal README with just the folder name
|
|
644
|
-
5. **Configuration Creation**: Creates temporary `pyproject.toml` with:
|
|
645
|
-
- Subfolder-specific package name (derived or custom)
|
|
646
|
-
- Specified version
|
|
647
|
-
- Correct package path for hatchling
|
|
648
|
-
6. **Build Execution**: Runs build command with all dependencies in place
|
|
649
|
-
7. **Cleanup**: Restores original `pyproject.toml` and removes temporary `__init__.py`
|
|
650
|
-
|
|
651
|
-
## Requirements
|
|
652
|
-
|
|
653
|
-
- Python >= 3.11
|
|
654
|
-
- **For publishing**: `twine` is required (install with `pip install twine`)
|
|
655
|
-
- **For secure credential storage**: `keyring` is optional but recommended (install with `pip install keyring`)
|
|
656
749
|
|
|
657
750
|
## Development
|
|
658
751
|
|
|
@@ -673,7 +766,7 @@ uv run pytest
|
|
|
673
766
|
make lint
|
|
674
767
|
```
|
|
675
768
|
|
|
676
|
-
### Project Structure
|
|
769
|
+
### Project Structure
|
|
677
770
|
|
|
678
771
|
```
|
|
679
772
|
python-package-folder/
|
|
@@ -693,18 +786,10 @@ python-package-folder/
|
|
|
693
786
|
└── pyproject.toml
|
|
694
787
|
```
|
|
695
788
|
|
|
696
|
-
## License
|
|
789
|
+
## License <!-- omit from toc -->
|
|
697
790
|
|
|
698
791
|
MIT License - see LICENSE file for details
|
|
699
792
|
|
|
700
|
-
## Contributing
|
|
793
|
+
## Contributing <!-- omit from toc -->
|
|
701
794
|
|
|
702
795
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
703
|
-
|
|
704
|
-
## Author
|
|
705
|
-
|
|
706
|
-
Alessio Lombardi - [GitHub](https://github.com/alelom)
|
|
707
|
-
|
|
708
|
-
## Related Projects
|
|
709
|
-
|
|
710
|
-
- [sysappend](https://pypi.org/project/sysappend/) - Flexible import management for Python projects
|