3.9 KiB
MWE Modern Python Projects
This is the minimum working example that I know of for a python package structure post-PEP517/8 and with pip/setuptools now allowing editable installs without a setup.py.
Motivation
Python packaging can get very complicated. Sometimes it's easier to just have a working example you can copy from.
Do you have one or more python files that you've written and would sure like to use, but also find that:
- "It works" in your IDE but when you run python elsewhere you get
ModuleNotFound
errors? - Someone has told you that your code "should be a package" so that "i can pip install it"?
and you're at loose ends about what to do next?
This worked example of a repo might help.
TL;DR
- Put package information, build information, and entry points in
pyproject.toml
- You can now install (and in editable mode even!) your code and its python dependencies with pip.
- You can make CLI entry points that act like normal programs
- You can share your code with others, or include it as a dependency in other projects.
So what are these files?
convenience boilerplate
.gitignore
Makefile
.pre-commit-config.yaml
README.org
These files are optional: they are not required as far as python is concerned. They are conveniences for the developer that I have come around over the years to converging on. I develop almost exclusively on linux (distro largely agnostic) and macOS.
- Using the lovely gitignore.io collection of recommended ignores, I've populated
a .gitignore
for python and common editors.
- If you like to automate the creation of venvs and installation and whatnot with
make
, as I do, then the included Makefile
may be useful.
- Automatic code formatting with
black
andisort
can be accomplished with.pre-commit-config.yaml
if pre-commit is used.
- And this file is
Readme.org
; choose a markup language of your preference.
package files
├── pyproject.toml
├── template
│ ├── __main__.py
│ └── __init__.py
pyproject.toml
specifies everything pip
needs to install a package,
including its name and metadata, what dependencies it requires, what entry
points to create, what extras may be available, and how it should be built.
This information has historically (and may still be, if you like) spread across
some combination of {pyproject.toml, setup.cfg, setup.py}
. Following
adoption of PEP517/517 by setuptools
, though, you can ram it all into
pyproject.toml
.
template/
is the directory of the python package. The name of the package is
template
, it has two modules __init__
and __main__
, and __main__
has
one function main
, which is configured as the entry point, and simply prints
"Hello,world!"
.
__init__.py
flags the directorytemplate/
as a python package. You need to have this file in any package (read: directory) or sub-package.__main__.py
permits callers to invoke your code withpython -m template
; when that is done, the__main__.py
module is executed. Consequently, it contains anif __name__ =
"main"= test, which calls the same callable (the functionmain()
) that the entrypoint specified inpyproject.toml
does. Add as many other modules, packages, etc. as you like undertemplate/
.
tests
├── tests
│ ├── __init__.py
│ └── test_template.py
pytest
is my preferred testing framework for python. It's listed as a
development dependency in pyproject.toml
.
- tests live in a directory called
tests
- invoking
./venv/bin/pytest ./tests
runs all python files inside tests whose filenames start withtest_
. - each function inside those files whose name begins with
test_
is treated as an independent unit test and is run. - see ned batchelder's excellent pytest primer for more detail.