Creating a Source Distribution and Wheel

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:

appendices/packaging/some_package_proj_v2/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:

appendices/packaging/some_package_proj_v2/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:

appendices/packaging/some_package_proj_v2/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:

appendices/packaging/some_package_proj_v2/CHANGELOG.rst
 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.