For personal use, the configuration shown in the previous section is enough to create a source distribution and a wheel. Let’s try it:
| $ cd /path/to/code/appendices/packaging/some_package_proj/ |
| $ python setup.py sdist bdist_wheel |
| running sdist |
| ... |
| warning: sdist: standard file not found: |
| should have one of README, README.rst, README.txt |
| |
| running check |
| warning: check: missing required meta-data: url |
| |
| warning: check: missing meta-data: |
| either (author and author_email) |
| or (maintainer and maintainer_email) must be supplied |
| |
| running bdist_wheel |
| ... |
| $ ls dist |
| some_package-0.0.0-py3-none-any.whl some_package-0.0.0.tar.gz |
Well, with some warnings, a .whl and a .tar.gz file are created. Let’s get rid of those warnings.
To do that, we need to:
Let’s also add:
It makes sense that you’d want these things. Including some kind of README allows people to know how to use the package. The url, author, and author_email (or maintainer) information makes sense to let users know who to contact if they have issues or questions about the package. A license is important to let people know how they can distribute, contribute, and reuse the package. And if it’s not open source, say so in the license data. To choose a license for open source projects, I recommend looking at https://choosealicense.com.
Those extra bits don’t add too much work. Here’s what I’ve come up with for a minimal default.
The setup.py:
| from setuptools import setup, find_packages |
| |
| setup( |
| name='some_package', |
| description='Demonstrate packaging and distribution', |
| |
| version='1.0', |
| author='Brian Okken', |
| author_email='brian@pythontesting.net', |
| url='https://pragprog.com/book/bopytest/python-testing-with-pytest', |
| |
| packages=find_packages(where='src'), |
| package_dir={'': 'src'}, |
| ) |
You should put the terms of the licensing in a LICENSE file. All of the code in this book follows the following license:
| Copyright (c) 2017 The Pragmatic Programmers, LLC |
| |
| All rights reserved. |
| |
| Copyrights apply to this source code. |
| |
| You may use the source code in your own projects, however the source code |
| may not be used to create commercial training material, courses, books, |
| articles, and the like. We make no guarantees that this source code is fit |
| for any purpose. |
Here’s the README.rst:
| ==================================================== |
| some_package: Demonstrate packaging and distribution |
| ==================================================== |
| |
| ``some_package`` is the Python package to demostrate how easy it is |
| to create installable, maintainable, shareable packages and distributions. |
| |
| It does contain one function, called ``some_func()``. |
| |
| .. code-block |
| |
| >>> import some_package |
| >>> some_package.some_func() |
| 42 |
| |
| |
| That's it, really. |
The README.rst is formatted in reStructuredText.[55] I’ve done what many have done before me: I copied a README.rst from an open source project, removed everything I didn’t like, and changed everything else to reflect this project.
You can also use an ASCII-formatted README.txt or README, but I’m okay with copy/paste/edit in this instance.
I recommend also adding a change log. Here’s the start of one:
| Changelog |
| ========= |
| |
| ------------------------------------------------------ |
| |
| 1.0 |
| --- |
| |
| Changes: |
| ~~~~~~~~ |
| |
| - Initial version. |
See http://keepachangelog.com for some great advice on what to put in your change log. All of the changes to tasks_proj over the course of this book have been logged into a CHANGELOG.rst file.
Let’s see if this was enough to remove the warnings:
| $ cd /path/to/code/appendices/packaging/some_package_proj_v2 |
| $ python setup.py sdist bdist_wheel |
| running sdist |
| running build |
| running build_py |
| creating build |
| creating build/lib |
| creating build/lib/some_package |
| copying src/some_package/__init__.py |
| -> build/lib/some_package |
| copying src/some_package/some_module.py |
| -> build/lib/some_package |
| installing to build/bdist.macosx-10.6-intel/wheel |
| running install |
| running install_lib |
| creating build/bdist.macosx-10.6-intel |
| creating build/bdist.macosx-10.6-intel/wheel |
| creating build/bdist.macosx-10.6-intel/wheel/some_package |
| copying build/lib/some_package/__init__.py |
| -> build/bdist.macosx-10.6-intel/wheel/some_package |
| copying build/lib/some_package/some_module.py |
| -> build/bdist.macosx-10.6-intel/wheel/some_package |
| running install_egg_info |
| Copying src/some_package.egg-info to |
| build/bdist.macosx-10.6-intel/wheel/some_package-1.0-py3.6.egg-info |
| running install_scripts |
| creating build/bdist.macosx-10.6-intel/wheel/some_package-1.0.dist-info/WHEEL |
| |
| $ ls dist |
| some_package-1.0-py3-none-any.whl some_package-1.0.tar.gz |
Yep. No warnings.
Now, we can put the .whl and/or .tar.gz files in a local shared directory and pip install to our heart’s content:
| $ cd /path/to/code/appendices/packaging/some_package_proj_v2 |
| $ mkdir ~/packages/ |
| $ cp dist/some_package-1.0-py3-none-any.whl ~/packages |
| $ cp dist/some_package-1.0.tar.gz ~/packages |
| $ pip install --no-index --find-links=~/packages some_package |
| Collecting some_package |
| Installing collected packages: some-package |
| Successfully installed some-package-1.0 |
| $ pip install --no-index --find-links=./dist some_package==1.0 |
| Requirement already satisfied: some_package==1.0 in |
| /path/to/venv/lib/python3.6/site-packages |
| $ |
Now you can create your own stash of local project packages from your team, including multiple versions of each, and install them almost as easily as packages from PyPI.