Reference · Packaging
Compressed from Lesson 0002. Built to be printed and kept beside the keyboard.
| Question | Command |
|---|---|
| What is in the wheel? | unzip -l dist/*.whl |
| What is in the sdist? | tar tzvf dist/*.tar.gz |
| Read a file without unpacking | unzip -p dist/*.whl pippen-1.0.0.dist-info/METADATA |
| Does it carry a given file? | unzip -l dist/*.whl | grep py.typed |
| Build both artifacts | uv build |
| Check metadata before upload | uvx twine check dist/* |
| Repository | sdist | Wheel | |
|---|---|---|---|
| Package code | src/pippen/ |
pippen-1.0.0/src/pippen/ |
pippen/ |
| Shipped tables | src/pippen/_data/ |
pippen-1.0.0/src/pippen/_data/ |
pippen/_data/ |
| Metadata | pyproject.toml |
pippen-1.0.0/PKG-INFO |
pippen-1.0.0.dist-info/ |
| Tests | tests/ |
included | absent |
The wheel is the installed layout. src/ exists to keep the
working directory off sys.path during development and has no meaning
after installation.
dist-info| File | Holds | Used by |
|---|---|---|
METADATA | Name, version, dependencies, README | pip resolver, PyPI page |
RECORD | Every installed file, with hash and size | pip uninstall |
WHEEL | Wheel format version, whether pure Python | The installer |
entry_points.txt | Console scripts, such as pippen | Script generation at install |
pippen-1.0.0-py3-none-any.whl
│ │ │ │ │
│ │ │ │ └─ platform tag: any OS
│ │ │ └────── ABI tag: no compiled extensions
│ │ └────────── Python tag: any Python 3
│ └──────────────── version
└─────────────────────── distribution name
A wheel tagged py3-none-any is a pure-Python wheel: one
file that installs everywhere. A package with compiled C would instead publish one
wheel per platform, such as cp310-manylinux_2_17_x86_64.
[tool.hatch.build.targets.wheel]
packages = ["src/pippen"]
artifacts = ["src/pippen/_data/*.parquet"]
[tool.hatch.build.targets.sdist]
include = ["src/", "tests/", "README.md", "LICENSE"]
The two targets are configured separately and do not inherit from each other. Declaring one and forgetting the other yields a package that works from a wheel and fails for anyone building from source.
from importlib.resources import as_file, files
resource = files("pippen").joinpath("_data").joinpath(filename)
with as_file(resource) as path:
return pd.read_parquet(path)
Never compute a path from __file__. An installed package is not
guaranteed to be a directory of loose files: it can be inside a zip, and
files() asks the loader that actually imported the module where its
resources are. as_file() materialises a real path when a library needs
one, and cleans up afterwards if extraction was required.
| Change | Bump | Promise |
|---|---|---|
| Bug fix, no API change | 0.1.1 | Safe upgrade |
| New function added | 0.2.0 | Safe upgrade, more available |
| Signature or behaviour changed | 1.0.0 | Callers may break |
Below 1.0.0 the guarantee is weaker by convention: a minor bump may break
callers, which is what 0.x communicates. That is why the README says
the API is 0.x and may still change.
A version published to PyPI can be yanked or deleted, and can never be re-uploaded under the same number. Publishing is append-only.