And I gave up trying.
Here is our project: https://gitlab.com/g2lab/cogran-python
- There is a python package in
cogran/
. - There are scripts in
scripts/
, one CLI interface, one QT GUI. - There are docs in
docs/
.
The GUI uses images and text from the docs for live help.
So we need to make sure that our docs
directory is included when packaging this all for others to install.
I tried for two days to make sense of the jungle of Python packaging. StackOverflow is full of non-explanatory half-answers, there are about 7 APIs implemented by 35 different projects with 42 different documentations and 98 different versions. Either I massively failed to find the one, wonderful, canonical, documented approach or this really is neither explicit nor beautiful.
So I give up. Can you help me out?
Do we need to integrate the docs into the actual package? Should we instead have three separate things: The cogran module package, a cogran-cli package, a cogran-gui package?
The setup
For building I used
rm -r build/ dist/ cogran.egg-info/ ; \
python setup.py sdist && \
python setup.py bdist_wheel
For looking into the resulting archives I used
watch tar tvf dist/cogran-0.1.0.tar.gz
and
watch unzip -t dist/cogran-0.1.0-py3-none-any.whl
For installing I used
pip uninstall --yes cogran ; \
pip install --no-cache-dir --user --upgrade dist/cogran-0.1.0.tar.gz && \
find ~/.local/ | grep cogran
and
pip uninstall --yes cogran ; \
pip install --no-cache-dir --user --upgrade dist/cogran-0.1.0-py3-none-any.whl && \
find ~/.local/ | grep cogran
What happens
sdist includes all kinds of stuff like the .gitignore and the examples directory. Both should not be included at the moment. Installing the resulting cogran-0.1.0.tar.gz will just install the package (cogran/__init__.py) and the scripts. Nothing else.
bdist_wheel only includes the package and the scripts. Nothing else.
Adding a MANIFEST.in
After fucking around with many iterations of fruitless attempts of writing a MANIFEST.in file that beautifully builds upon the someone-said default includes I gave in and wrote a very explicit one: https://gitlab.com/snippets/1850708
global-exclude *
graft cogran
graft docs
graft scripts
graft tests
include README.md
include LICENSE
include setup.py
include requirements.txt
global-exclude __pycache__
global-exclude *.py[co]
Now sdist includes all the stuff I want and nothing I do not want. Excellent. Installing the tar.gz however again only installed the package and the scripts.
The wheel again only had the package and the scripts inside.
setup.py: include_package_data=True
You just need to add include_package_data=True to your setup.py, people said. So I did. And no, that would only work for things inside our package, not directories on the same level. So this would change nothing.
setup.py: data_files
Supply the paths to the files in a data_files list, someone said. So I added an explicit list (at this stage I just wanted it to work, who cares about manual effort, future breakage and ugliness):
data_files = [
("docs", (
"docs/images/Areal Weighting.png",
"docs/images/Attribute Weighting.png",
"docs/images/postcodes vs districts.png",
"docs/images/Binary Dasymetric Weighting.png",
"docs/images/N-Class Dasymetric Weighting.png",
"docs/help.html"
))
]
And yes, now these files end up in the wheel. But guess what, they still do not get installed anywhere
setup.py: package_data
https://github.com/pypa/sampleproject/blob/master/setup.py#L164 like include_package_data only applies to data inside the package, our’s is on the side.