setup.py - Package and install CodeChat¶
Builds and installs CodeChat.
Packaging notes¶
Packaging on Python is a mess, IMHO. It takes an easy job and makes it hard.
A quick summary: distutils can’t install dependencies from PyPI, so use setuptools. A source distribution is a good idea becaues it can run on a bare Python installation with no other installs required, but there’s no standard format (.zip?, .tar.gz?, etc.). An .egg is nice, but requires setuptools/pip/ez_setup installed. The .whl (Python wheel) is the latest and greatest format that superceeds eggs, but with similar problems (requires wheel to be installed).
Reading to get up to speed:
- Python Packaging User Guide - the most up-to-date reference I’ve found so far. Tells which tools to actually use.
- distutils - The built-in installer. Tells what to do, but not what actually happens. It doesn’t have the ability to install dependencies from PyPI, which I need.
- setuptools - A distutils replacement which can install dependencies, so I use it.
To package¶
Create a source distribution, a built distribution, then upload both to CodeChat at PyPI:
python setup.py sdist bdist_wheel upload
To upload docs, which are placed here (make sure to run Sphinx first, so the docs will be current):
python setup.py upload_docs --upload-dir=_build\html
For development:
python setup.py develop
Yajo helped package this for Linux. Thanks so much. See also python-codechat.spec - openSUSE Build Service packaging file. Unfortunately, the Linux packaging is untested.
Packaging script¶
Otherwise known as the evils of setup.py
.
For users who install this from source but don’t have setuptools installed, auto-install it. When packaging for Linux, downloads are blocked so we must specify a very old already-installed version. Leave this as a patch so that we normally use a more modern version.
import ez_setup
ez_setup.use_setuptools()
PyPA copied code¶
From PyPA’s sample setup.py,
read long_description
from a file. This code was last updated on
26-May-2015 based on this commit.
Always prefer setuptools over distutils
from setuptools import setup, find_packages
To use a consistent encoding
from codecs import open
from os import path
Imports for version parse code.
import sys
import os
import re
import io
here = path.abspath(path.dirname(__file__))
Get the long description from the relevant file.
with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
long_description = f.read()
The inclusion of a raw tag causes PyPI to not render the reST. Ouch. Remove it before uploading.
long_description = re.sub('\.\. raw.*<\/iframe>', '', long_description, flags=re.DOTALL)
This code was copied from version parse code. See version
in the call
to setup
below.
def read(*names, **kwargs):
with io.open(
os.path.join(os.path.dirname(__file__), *names),
encoding=kwargs.get("encoding", "utf8")
) as fp:
return fp.read()
def find_version(*file_paths):
version_file = read(*file_paths)
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
version_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string.")
My code¶
We support Python 3.3 and higher.
assert sys.version_info >= (3, 3)
setup(
This must comply with PEP 0426‘s name requirements.
name='CodeChat',
Projects should comply with the version scheme
specified in PEP440. I use this so that my Sphinx docs will have the same
version number. There are a lot of alternatives in Single-sourcing the
Project Version.
While I like something simple, such as import CodeChat
then
version=CodeChat.__version__
here, this means any dependeninces of
__init__.py will be requred to run setup,
a bad thing. So, instead I read the file in setup.py
and parse the
version with a regex (see version parse code).
version=find_version("CodeChat", "__init__.py"),
description="The CodeChat system for software documentation",
long_description=long_description,
The project’s main homepage.
url='https://pythonhosted.org/CodeChat/README.html',
Obscure my e-mail address to help defeat spam-bots.
author="Bryan A. Jones",
author_email="bjones AT ece.msstate.edu",
license='GPLv3+',
These are taken from the full list.
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Operating System :: OS Independent',
'Natural Language :: English',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Topic :: Software Development :: Documentation',
'Topic :: Text Processing :: Markup',
],
keywords='literate programming',
packages=['CodeChat'],
List run-time dependencies here. These will be installed by pip when your project is installed. For an analysis of “install_requires” vs pip’s requirements files see: https://packaging.python.org/en/latest/requirements.html
install_requires=(
Enum was introduced in Python 3.4. Use a backport of it if needed.
(['enum34'] if sys.version_info.minor == 3 else [])
Note: I don’t include Sphinx in this list: while CodeToRest.py can be executed from the command line if the packages below are installed, CodeToRestSphinx.py can only be executed by Sphinx.
+ ['docutils>=0.12',
'pygments>=2.1']),
List additional groups of dependencies here (e.g. development dependencies). You can install these using the following syntax, for example:
$ pip install -e .[test]
extras_require={
'test': ['pytest'],
},
To package data files, I’m using include_package_data=True
then
putting the files in MANIFEST.in. See including data
files.
include_package_data=True,
)