python-skills 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.
- python_skills/__init__.py +10 -0
- python_skills/__main__.py +6 -0
- python_skills/adapters/__init__.py +48 -0
- python_skills/adapters/agent_skills.py +415 -0
- python_skills/adapters/aider_adapter.py +226 -0
- python_skills/adapters/base.py +153 -0
- python_skills/adapters/claude.py +474 -0
- python_skills/adapters/cline.py +332 -0
- python_skills/adapters/codex.py +24 -0
- python_skills/adapters/continue_adapter.py +198 -0
- python_skills/adapters/cursor.py +327 -0
- python_skills/adapters/gemini.py +26 -0
- python_skills/adapters/goose.py +26 -0
- python_skills/adapters/junie.py +25 -0
- python_skills/adapters/kiro.py +382 -0
- python_skills/adapters/opencode.py +27 -0
- python_skills/adapters/roo.py +25 -0
- python_skills/adapters/universal.py +203 -0
- python_skills/adapters/vscode.py +27 -0
- python_skills/adapters/windsurf.py +26 -0
- python_skills/adapters/zed.py +27 -0
- python_skills/cli.py +326 -0
- python_skills/config.py +160 -0
- python_skills/detector.py +152 -0
- python_skills/installer.py +163 -0
- python_skills/markers.py +115 -0
- python_skills/skills/__init__.py +14 -0
- python_skills/skills/loader.py +171 -0
- python_skills/skills/metadata.py +152 -0
- python_skills/skills/registry.py +101 -0
- python_skills/state.py +204 -0
- python_skills-1.0.0.dist-info/METADATA +99 -0
- python_skills-1.0.0.dist-info/RECORD +105 -0
- python_skills-1.0.0.dist-info/WHEEL +4 -0
- python_skills-1.0.0.dist-info/entry_points.txt +2 -0
- python_skills-1.0.0.dist-info/licenses/LICENSE +21 -0
- skills/advanced_python.md +239 -0
- skills/anti_patterns/index.md +406 -0
- skills/comprehensions.md +167 -0
- skills/control_flow.md +175 -0
- skills/data_structures.md +243 -0
- skills/debugging/common_bugs.md +222 -0
- skills/debugging/inspection_techniques.md +249 -0
- skills/debugging/root_cause.md +203 -0
- skills/engineering/application_logging.md +195 -0
- skills/engineering/cli_apps.md +207 -0
- skills/engineering/configuration.md +218 -0
- skills/engineering/database.md +240 -0
- skills/engineering/dependency_management.md +205 -0
- skills/engineering/http_clients.md +267 -0
- skills/engineering/modules_packages.md +211 -0
- skills/engineering/packaging.md +197 -0
- skills/engineering/project_structure.md +155 -0
- skills/engineering/pyproject_toml.md +302 -0
- skills/engineering/virtual_environments.md +206 -0
- skills/functions.md +244 -0
- skills/generation/async_concurrency.md +291 -0
- skills/generation/error_handling.md +276 -0
- skills/generation/protocols_generics.md +243 -0
- skills/generation/type_hints.md +290 -0
- skills/generation/validation_pipeline.md +274 -0
- skills/generation/workflow.md +190 -0
- skills/oop.md +228 -0
- skills/quality/abstractions.md +154 -0
- skills/quality/comments.md +177 -0
- skills/quality/documentation.md +176 -0
- skills/quality/duplication.md +137 -0
- skills/quality/maintainability.md +142 -0
- skills/quality/naming.md +171 -0
- skills/quality/quality_functions.md +245 -0
- skills/quality/readability.md +239 -0
- skills/quality/type_annotations.md +192 -0
- skills/refactoring/behavior_preservation.md +157 -0
- skills/refactoring/incremental.md +187 -0
- skills/refactoring/interface_stability.md +199 -0
- skills/refactoring/safe_refactoring.md +206 -0
- skills/security/auth_boundaries.md +200 -0
- skills/security/command_injection.md +207 -0
- skills/security/dependency_risks.md +282 -0
- skills/security/file_handling.md +156 -0
- skills/security/input_validation.md +190 -0
- skills/security/path_traversal.md +172 -0
- skills/security/secrets.md +171 -0
- skills/security/sql_injection.md +188 -0
- skills/security/unsafe_deserialization.md +164 -0
- skills/stdlib/argparse.md +178 -0
- skills/stdlib/collections.md +212 -0
- skills/stdlib/datetime.md +187 -0
- skills/stdlib/functools.md +238 -0
- skills/stdlib/itertools.md +183 -0
- skills/stdlib/json.md +162 -0
- skills/stdlib/logging.md +185 -0
- skills/stdlib/os_sys.md +184 -0
- skills/stdlib/pathlib.md +218 -0
- skills/stdlib/re.md +171 -0
- skills/stdlib/statistics.md +112 -0
- skills/stdlib/subprocess.md +211 -0
- skills/testing/async_tests.md +249 -0
- skills/testing/coverage.md +168 -0
- skills/testing/edge_cases.md +197 -0
- skills/testing/fixtures_mocks.md +203 -0
- skills/testing/organization.md +205 -0
- skills/testing/parameterized.md +174 -0
- skills/testing/regression_tests.md +165 -0
- skills/variables_types.md +107 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Engineering: Modules and Packages
|
|
2
|
+
|
|
3
|
+
**Purpose**: Python module system, imports, and package organization.
|
|
4
|
+
|
|
5
|
+
**When to use**: Structuring code, managing imports, avoiding circular dependencies.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Rules
|
|
10
|
+
|
|
11
|
+
### Module vs Package
|
|
12
|
+
- **Module**: Single `.py` file
|
|
13
|
+
- **Package**: Directory with `__init__.py` (regular) or without (namespace, 3.3+)
|
|
14
|
+
|
|
15
|
+
### Import Styles
|
|
16
|
+
```python
|
|
17
|
+
# Absolute (preferred)
|
|
18
|
+
from package.module import Class
|
|
19
|
+
from package import module
|
|
20
|
+
|
|
21
|
+
# Relative (within package only)
|
|
22
|
+
from .module import Class
|
|
23
|
+
from ..parent import Class
|
|
24
|
+
from ... import top_level
|
|
25
|
+
|
|
26
|
+
# Import module (not names)
|
|
27
|
+
import package.module
|
|
28
|
+
package.module.function()
|
|
29
|
+
|
|
30
|
+
# Avoid
|
|
31
|
+
from module import * # Pollutes namespace, unclear dependencies
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### `__init__.py` Responsibilities
|
|
35
|
+
```python
|
|
36
|
+
# 1. Define public API
|
|
37
|
+
from .core import main_function
|
|
38
|
+
from .models import User
|
|
39
|
+
|
|
40
|
+
__all__ = ["main_function", "User"]
|
|
41
|
+
|
|
42
|
+
# 2. Optional: convenient imports
|
|
43
|
+
from .subpackage import feature # Re-export
|
|
44
|
+
|
|
45
|
+
# 3. Optional: version
|
|
46
|
+
try:
|
|
47
|
+
from ._version import __version__
|
|
48
|
+
except ImportError:
|
|
49
|
+
__version__ = "0.0.0"
|
|
50
|
+
|
|
51
|
+
# 4. Optional: initialization
|
|
52
|
+
import logging
|
|
53
|
+
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Private Modules
|
|
57
|
+
```python
|
|
58
|
+
# _private.py — convention: not part of public API
|
|
59
|
+
# __init__.py should not import from _private in __all__
|
|
60
|
+
|
|
61
|
+
# __dunder__.py — special Python modules (avoid creating)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Namespace Packages (PEP 420)
|
|
65
|
+
```python
|
|
66
|
+
# No __init__.py — multiple directories contribute to same package
|
|
67
|
+
# pkg_resources / importlib.metadata handles this
|
|
68
|
+
# Use for plugins, large orgs splitting packages
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Circular Import Prevention
|
|
74
|
+
|
|
75
|
+
### Causes
|
|
76
|
+
```python
|
|
77
|
+
# a.py
|
|
78
|
+
from b import B
|
|
79
|
+
class A: pass
|
|
80
|
+
|
|
81
|
+
# b.py
|
|
82
|
+
from a import A
|
|
83
|
+
class B: pass
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Solutions
|
|
87
|
+
1. **Refactor** — move shared code to third module
|
|
88
|
+
2. **Lazy import** — import inside function
|
|
89
|
+
3. **Type-only import** — `from __future__ import annotations` + `TYPE_CHECKING`
|
|
90
|
+
4. **Interface/Protocol** — depend on abstraction
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
# Type-only import (no runtime dependency)
|
|
94
|
+
from __future__ import annotations
|
|
95
|
+
from typing import TYPE_CHECKING
|
|
96
|
+
|
|
97
|
+
if TYPE_CHECKING:
|
|
98
|
+
from .other import OtherClass
|
|
99
|
+
|
|
100
|
+
def func(obj: OtherClass) -> None: # Only for type checking
|
|
101
|
+
...
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Import Best Practices
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
# Standard library first
|
|
110
|
+
import os
|
|
111
|
+
import sys
|
|
112
|
+
from pathlib import Path
|
|
113
|
+
from typing import Optional
|
|
114
|
+
|
|
115
|
+
# Third party
|
|
116
|
+
import requests
|
|
117
|
+
from pydantic import BaseModel
|
|
118
|
+
|
|
119
|
+
# Local (absolute)
|
|
120
|
+
from mypackage.core import process
|
|
121
|
+
from mypackage.models import User
|
|
122
|
+
|
|
123
|
+
# Local (relative) — only within same package
|
|
124
|
+
from . import utils
|
|
125
|
+
from ..config import settings
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Import Order (per PEP 8 / ruff)
|
|
129
|
+
1. Standard library
|
|
130
|
+
2. Third party
|
|
131
|
+
3. Local (absolute)
|
|
132
|
+
4. Local (relative)
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Dynamic Imports
|
|
137
|
+
```python
|
|
138
|
+
import importlib
|
|
139
|
+
|
|
140
|
+
# Import by name
|
|
141
|
+
module = importlib.import_module("package.module")
|
|
142
|
+
Class = getattr(module, "ClassName")
|
|
143
|
+
|
|
144
|
+
# Plugin pattern
|
|
145
|
+
def load_plugins(entry_point: str) -> list[Plugin]:
|
|
146
|
+
plugins = []
|
|
147
|
+
for ep in importlib.metadata.entry_points(group=entry_point):
|
|
148
|
+
plugins.append(ep.load())
|
|
149
|
+
return plugins
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Decision Rules
|
|
155
|
+
|
|
156
|
+
| Situation | Approach |
|
|
157
|
+
|-----------|----------|
|
|
158
|
+
| Public API | Define in `__init__.py` with `__all__` |
|
|
159
|
+
| Internal helper | Prefix with `_` (module or name) |
|
|
160
|
+
| Type-only dependency | `TYPE_CHECKING` guard |
|
|
161
|
+
| Plugin/extension | `importlib.metadata` entry points |
|
|
162
|
+
| Optional dependency | `try/except ImportError` |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Preferred Patterns
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
# Optional dependency
|
|
170
|
+
try:
|
|
171
|
+
import orjson as json
|
|
172
|
+
except ImportError:
|
|
173
|
+
import json
|
|
174
|
+
|
|
175
|
+
# Protocol for external dependency
|
|
176
|
+
class Cache(Protocol):
|
|
177
|
+
def get(self, key: str) -> bytes | None: ...
|
|
178
|
+
def set(self, key: str, value: bytes) -> None: ...
|
|
179
|
+
|
|
180
|
+
# Usage doesn't require import
|
|
181
|
+
def process(cache: Cache) -> None:
|
|
182
|
+
...
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Avoid
|
|
188
|
+
|
|
189
|
+
- `import *` (except `__all__` in `__init__.py`)
|
|
190
|
+
- Relative imports beyond one level (`from .... import`)
|
|
191
|
+
- Modifying `sys.path` at runtime
|
|
192
|
+
- Import side effects (code running on import)
|
|
193
|
+
- Circular imports (refactor instead)
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Validation Considerations
|
|
198
|
+
|
|
199
|
+
- `python -c "import package"` works
|
|
200
|
+
- Import time < 100ms (for CLI tools)
|
|
201
|
+
- No circular import warnings
|
|
202
|
+
- `mypy` passes with `--strict`
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Related Skills
|
|
207
|
+
|
|
208
|
+
- `engineering/project_structure.md`
|
|
209
|
+
- `engineering/pyproject_toml.md`
|
|
210
|
+
- `generation/type_hints.md` (TYPE_CHECKING)
|
|
211
|
+
- `generation/protocols_generics.md` (Protocol)
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Engineering: Packaging
|
|
2
|
+
|
|
3
|
+
**Purpose**: Building and distributing Python packages.
|
|
4
|
+
|
|
5
|
+
**When to use**: Publishing to PyPI, creating wheels, Docker images.
|
|
6
|
+
---
|
|
7
|
+
---
|
|
8
|
+
name: engineering_packaging
|
|
9
|
+
purpose: Building and distributing Python packages
|
|
10
|
+
category: engineering
|
|
11
|
+
triggers:
|
|
12
|
+
- packaging
|
|
13
|
+
- wheel
|
|
14
|
+
- sdist
|
|
15
|
+
- pypi
|
|
16
|
+
- twine
|
|
17
|
+
- build
|
|
18
|
+
- version
|
|
19
|
+
- docker
|
|
20
|
+
dependencies:
|
|
21
|
+
- engineering/pyproject_toml.md
|
|
22
|
+
- engineering/project_structure.md
|
|
23
|
+
- engineering/dependency_management.md
|
|
24
|
+
- engineering/virtual_environments.md
|
|
25
|
+
priority: primary
|
|
26
|
+
estimated_tokens: 1800
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Core Rules
|
|
30
|
+
|
|
31
|
+
### Build Process
|
|
32
|
+
```bash
|
|
33
|
+
# Modern (PEP 517/518)
|
|
34
|
+
pipx run build
|
|
35
|
+
# or
|
|
36
|
+
python -m build
|
|
37
|
+
|
|
38
|
+
# Outputs:
|
|
39
|
+
# dist/
|
|
40
|
+
# my_package-1.0.0.tar.gz (source distribution)
|
|
41
|
+
# my_package-1.0.0-py3-none-any.whl (wheel)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Wheel vs sdist
|
|
45
|
+
| Type | Contents | Use Case |
|
|
46
|
+
|------|----------|----------|
|
|
47
|
+
| Wheel (.whl) | Pre-compiled, metadata | Installation (fast) |
|
|
48
|
+
| sdist (.tar.gz) | Source code, pyproject.toml | Archival, source builds |
|
|
49
|
+
|
|
50
|
+
### Version Management
|
|
51
|
+
```toml
|
|
52
|
+
# pyproject.toml with hatchling
|
|
53
|
+
[tool.hatch.version]
|
|
54
|
+
source = "regex"
|
|
55
|
+
regex = '^__version__ = "(.+)"$'
|
|
56
|
+
path = "src/my_package/_version.py"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
# src/my_package/_version.py
|
|
61
|
+
__version__ = "1.2.3"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Publishing to PyPI
|
|
65
|
+
```bash
|
|
66
|
+
# Test PyPI first
|
|
67
|
+
pipx run twine upload --repository testpypi dist/*
|
|
68
|
+
|
|
69
|
+
# Production
|
|
70
|
+
pipx run twine upload dist/*
|
|
71
|
+
|
|
72
|
+
# With API token (CI)
|
|
73
|
+
twine upload -u __token__ -p $PYPI_TOKEN dist/*
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Package Metadata Check
|
|
77
|
+
```bash
|
|
78
|
+
twine check dist/*
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### PEP 561 (Typed Packages)
|
|
82
|
+
```toml
|
|
83
|
+
# pyproject.toml
|
|
84
|
+
[project]
|
|
85
|
+
# ... other config
|
|
86
|
+
|
|
87
|
+
# Include py.typed marker
|
|
88
|
+
[tool.hatch.build.targets.wheel]
|
|
89
|
+
packages = ["src/my_package"]
|
|
90
|
+
# Or use setuptools:
|
|
91
|
+
# [tool.setuptools.package-data]
|
|
92
|
+
# "*" = ["py.typed"]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Verify
|
|
97
|
+
pip install my-package
|
|
98
|
+
python -c "import my_package; print(my_package.__file__)"
|
|
99
|
+
# Should have py.typed in package root
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Decision Rules
|
|
105
|
+
|
|
106
|
+
| Goal | Tool |
|
|
107
|
+
|------|------|
|
|
108
|
+
| Build wheel/sdist | `build` (PEP 517) |
|
|
109
|
+
| Version management | `hatchling` / `setuptools-scm` |
|
|
110
|
+
| Publish to PyPI | `twine` |
|
|
111
|
+
| Docker image | Multi-stage build with `--target` |
|
|
112
|
+
| Monorepo | `hatch` environments or `pdm` |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Docker Packaging
|
|
117
|
+
|
|
118
|
+
```dockerfile
|
|
119
|
+
# Multi-stage build
|
|
120
|
+
FROM python:3.12-slim AS builder
|
|
121
|
+
WORKDIR /app
|
|
122
|
+
COPY pyproject.toml ./
|
|
123
|
+
COPY src/ ./src/
|
|
124
|
+
RUN pip install --no-cache-dir build && python -m build --wheel
|
|
125
|
+
|
|
126
|
+
FROM python:3.12-slim AS runtime
|
|
127
|
+
WORKDIR /app
|
|
128
|
+
COPY --from=builder /app/dist/*.whl ./
|
|
129
|
+
RUN pip install --no-cache-dir *.whl && rm *.whl
|
|
130
|
+
USER 1000
|
|
131
|
+
ENTRYPOINT ["my-cli"]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Preferred Patterns
|
|
137
|
+
|
|
138
|
+
```toml
|
|
139
|
+
# Complete pyproject.toml for packaging
|
|
140
|
+
[build-system]
|
|
141
|
+
requires = ["hatchling"]
|
|
142
|
+
build-backend = "hatchling.build"
|
|
143
|
+
|
|
144
|
+
[project]
|
|
145
|
+
name = "my-package"
|
|
146
|
+
dynamic = ["version"]
|
|
147
|
+
description = "Description"
|
|
148
|
+
readme = "README.md"
|
|
149
|
+
license = {text = "MIT"}
|
|
150
|
+
requires-python = ">=3.10"
|
|
151
|
+
dependencies = [...]
|
|
152
|
+
classifiers = [
|
|
153
|
+
"Programming Language :: Python :: 3",
|
|
154
|
+
"Programming Language :: Python :: 3.10",
|
|
155
|
+
"Programming Language :: Python :: 3.11",
|
|
156
|
+
"Programming Language :: Python :: 3.12",
|
|
157
|
+
"Typing :: Typed", # PEP 561
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
[tool.hatch.version]
|
|
161
|
+
source = "regex"
|
|
162
|
+
regex = '^__version__ = "(.+)"$'
|
|
163
|
+
path = "src/my_package/_version.py"
|
|
164
|
+
|
|
165
|
+
[tool.hatch.build.targets.wheel]
|
|
166
|
+
packages = ["src/my_package"]
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Avoid
|
|
172
|
+
|
|
173
|
+
- `setup.py` for new projects
|
|
174
|
+
- Manual version updates in multiple files
|
|
175
|
+
- Publishing without `twine check`
|
|
176
|
+
- Missing `requires-python`
|
|
177
|
+
- No `Typing :: Typed` classifier for typed packages
|
|
178
|
+
- Committing `dist/` to git
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Validation Considerations
|
|
183
|
+
|
|
184
|
+
- `twine check dist/*` passes
|
|
185
|
+
- `pip install dist/*.whl` works
|
|
186
|
+
- Package imports correctly
|
|
187
|
+
- Version matches expected
|
|
188
|
+
- Metadata complete on PyPI
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Related Skills
|
|
193
|
+
|
|
194
|
+
- `engineering/pyproject_toml.md`
|
|
195
|
+
- `engineering/project_structure.md`
|
|
196
|
+
- `engineering/dependency_management.md`
|
|
197
|
+
- `engineering/virtual_environments.md`
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Engineering: Project Structure
|
|
2
|
+
|
|
3
|
+
**Purpose**: Modern Python project layout conventions.
|
|
4
|
+
|
|
5
|
+
**When to use**: Creating new projects or understanding existing ones.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Rules
|
|
10
|
+
|
|
11
|
+
### Recommended Layout (src-layout)
|
|
12
|
+
```
|
|
13
|
+
project-root/
|
|
14
|
+
├── pyproject.toml # Project metadata, dependencies, tool config
|
|
15
|
+
├── README.md
|
|
16
|
+
├── LICENSE
|
|
17
|
+
├── src/
|
|
18
|
+
│ └── package_name/ # Actual package (matches distribution name)
|
|
19
|
+
│ ├── __init__.py
|
|
20
|
+
│ ├── _version.py # Optional: version from setuptools-scm
|
|
21
|
+
│ ├── module.py
|
|
22
|
+
│ ├── subpackage/
|
|
23
|
+
│ │ ├── __init__.py
|
|
24
|
+
│ │ └── ...
|
|
25
|
+
│ └── py.typed # Marker for PEP 561 (if typed)
|
|
26
|
+
├── tests/
|
|
27
|
+
│ ├── __init__.py
|
|
28
|
+
│ ├── conftest.py # Pytest fixtures
|
|
29
|
+
│ ├── unit/
|
|
30
|
+
│ │ └── test_module.py
|
|
31
|
+
│ ├── integration/
|
|
32
|
+
│ └── fixtures/
|
|
33
|
+
├── docs/
|
|
34
|
+
├── scripts/ # Utility scripts
|
|
35
|
+
├── .github/
|
|
36
|
+
│ └── workflows/ # CI/CD
|
|
37
|
+
├── .gitignore
|
|
38
|
+
├── .pre-commit-config.yaml
|
|
39
|
+
└── docker/ # Docker files (optional)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Flat Layout (Simple Projects)
|
|
43
|
+
```
|
|
44
|
+
project-root/
|
|
45
|
+
├── pyproject.toml
|
|
46
|
+
├── package_name/
|
|
47
|
+
│ ├── __init__.py
|
|
48
|
+
│ └── ...
|
|
49
|
+
├── tests/
|
|
50
|
+
└── ...
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Key Principles
|
|
54
|
+
|
|
55
|
+
1. **src-layout preferred** — separates package from project config, prevents accidental imports
|
|
56
|
+
2. **Package name = distribution name** — `src/my_package` → `pip install my-package`
|
|
57
|
+
3. **Tests outside package** — `tests/` not `src/package/tests/`
|
|
58
|
+
4. **Single top-level package** — avoid multiple packages in one repo
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Module Organization
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
src/package/
|
|
66
|
+
├── __init__.py # Public API exports
|
|
67
|
+
├── _internal.py # Private (leading underscore)
|
|
68
|
+
├── core.py # Core functionality
|
|
69
|
+
├── models.py # Data models
|
|
70
|
+
├── services.py # Business logic
|
|
71
|
+
├── api.py # External interfaces
|
|
72
|
+
├── cli.py # CLI entry point
|
|
73
|
+
├── config.py # Configuration
|
|
74
|
+
├── exceptions.py # Custom exceptions
|
|
75
|
+
└── utils.py # Utilities (avoid if possible)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `__init__.py` Pattern
|
|
79
|
+
```python
|
|
80
|
+
# src/package/__init__.py
|
|
81
|
+
"""Package docstring."""
|
|
82
|
+
|
|
83
|
+
from .core import main_function
|
|
84
|
+
from .models import User, Config
|
|
85
|
+
from .exceptions import PackageError
|
|
86
|
+
|
|
87
|
+
__version__ = "1.0.0"
|
|
88
|
+
__all__ = [
|
|
89
|
+
"main_function",
|
|
90
|
+
"User",
|
|
91
|
+
"Config",
|
|
92
|
+
"PackageError",
|
|
93
|
+
]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Decision Rules
|
|
99
|
+
|
|
100
|
+
| Project Type | Layout |
|
|
101
|
+
|--------------|--------|
|
|
102
|
+
| Library (published to PyPI) | src-layout |
|
|
103
|
+
| Application (Docker, server) | src-layout or flat |
|
|
104
|
+
| Simple script | Flat (single file or package) |
|
|
105
|
+
| Monorepo (multiple packages) | `packages/pkg1/`, `packages/pkg2/` |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Preferred Patterns
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
# src/package/_version.py (for setuptools-scm)
|
|
113
|
+
# This file is generated at build time
|
|
114
|
+
__version__ = "0.0.0"
|
|
115
|
+
|
|
116
|
+
# src/package/__init__.py
|
|
117
|
+
try:
|
|
118
|
+
from ._version import __version__
|
|
119
|
+
except ImportError:
|
|
120
|
+
__version__ = "0.0.0"
|
|
121
|
+
|
|
122
|
+
from .core import main
|
|
123
|
+
from .models import User
|
|
124
|
+
|
|
125
|
+
__all__ = ["main", "User", "__version__"]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Avoid
|
|
131
|
+
|
|
132
|
+
- Multiple top-level packages in one distribution
|
|
133
|
+
- Putting tests inside the package
|
|
134
|
+
- `__init__.py` with heavy imports (slow import)
|
|
135
|
+
- Circular imports between modules
|
|
136
|
+
- Deep nesting (>3 levels)
|
|
137
|
+
- Non-package directories in `src/`
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Validation Considerations
|
|
142
|
+
|
|
143
|
+
- `pip install -e .` works correctly
|
|
144
|
+
- `python -m package` runs `__main__.py` if present
|
|
145
|
+
- Import time is fast
|
|
146
|
+
- `build` produces correct wheel
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Related Skills
|
|
151
|
+
|
|
152
|
+
- `engineering/pyproject_toml.md`
|
|
153
|
+
- `engineering/modules_packages.md`
|
|
154
|
+
- `engineering/packaging.md`
|
|
155
|
+
- `engineering/dependency_management.md`
|