Overview
The module pyTooling.Packaging
provides helper functions to achieve a single-source-of-truth Python package
description, where (almost) no information is duplicated. The main idea is to read configuration files, READMEs, and
Python source files from setup.py
, so it doesn’t duplicate information. This allows an easier the maintenance of
Python packages.
Helper Functions
The following helper functions are used by DescribePythonPackage()
, but these can also be
called individually to reuse internal features offered by that package description function.
loadReadmeFile
The function loadReadmeFile()
reads a README
file and guesses the contents MIME type on
the file’s extension. It returns an instance of Readme
.
This read text can then be used for the package’s long description.
Usage in a setup.py
from pathlib import Path
from pyTooling.Packaging import loadReadmeFile
readmeFile = Path("README.md")
readme = loadReadmeFile(readmeFile)
# print(readme.Content)
# print(readme.MimeType)
README.md
# pyTooling
**pyTooling** is a powerful collection of arbitrary useful abstract data models, missing classes,
decorators, a new performance boosting meta-class and enhanced exceptions. It also provides lots of helper
functions e.g. to ease the handling of package descriptions or to unify multiple existing APIs into a single
API.
loadRequirementsFile
The function loadRequirementsFile()
recursively reads a requirements.txt
file and
extracts all specified dependencies. As a result, a list of requirement strings is returned.
Warning
The returned list might contain duplicates, which should be removed before further processing.
This can be achieve by converting the result to a set
and back to a list
.
requirements = list(set(loadRequirementsFile(requirementsFile)))
Usage in a setup.py
from pathlib import Path
from pyTooling.Packaging import loadRequirementsFile
requirementsFile = Path("doc/requirements.txt")
requirements = loadRequirementsFile(requirementsFile)
# for req in requirements:
# print(req)
requirements.txt
-r ../requirements.txt
Sphinx ~= 8.1
docutils <= 0.21
sphinx_rtd_theme ~= 3.0
extractVersionInformation
The function extractVersionInformation()
extracts version information from a Python source
file (module). Usually these module variables are defined in a __init__.py
file.
Supported fields
Author name (
__author__
)Author email address (
__email__
)Copyright information (
__copyright_
)License name (
__license__
)Version number (
__version__
)Keywords (
__keywords__
)
The function returns an instance of VersionInformation
, which offers the gathered
information as properties.
Usage in setup.py
from setuptools import setup
from pyTooling.Packaging import extractVersionInformation
file = Path("./pyTooling/Common/__init__.py")
versionInfo = extractVersionInformation(file)
setup(
# ...
version=versionInformation.Version,
author=versionInformation.Author,
author_email=versionInformation.Email,
keywords=versionInformation.Keywords,
# ...
)
__init__.py
__author__ = "Patrick Lehmann"
__email__ = "Paebbels@gmail.com"
__copyright__ = "2017-2024, Patrick Lehmann"
__license__ = "Apache License, Version 2.0"
__version__ = "1.10.1"
__keywords__ = ["decorators", "meta classes", "exceptions", "platform", "versioning"]
PackageDescriptions
DescribePythonPackage
DescribePythonPackage()
is a helper function to describe a Python package. The result is a
dictionary that can be handed over to setuptools.setup()
. Some information will be gathered implicitly from
well-known files (e.g. README.md
, requirements.txt
, __init__.py
).
Handling of namespace packages
If parameter packageName
contains a dot, a namespace package is assumed. Then
setuptools.find_namespace_packages()
is used to discover package files.
Otherwise, the package is considered a normal package and setuptools.find_packages()
is used.
In both cases, the following packages (directories) are excluded from search:
build
,build.*
dist
,dist.*
doc
,doc.*
tests
,tests.*
Handling of minimal Python version
The minimal required Python version is selected from parameter pythonVersions
.
Handling of dunder variables
A Python source file specified by parameter sourceFileWithVersion
will be analyzed with Pythons parser and the
resulting AST will be searched for the following dunder variables:
__author__
:str
__copyright__
:str
__email__
:str
__keywords__
:typing.Iterable`[:class:`str
]__license__
:str
__version__
:str
The gathered information be used to add further mappings in the result dictionary.
Handling of package classifiers
To reduce redundantly provided parameters to this function (e.g. supported pythonVersions
), only additional
classifiers should be provided via parameter classifiers
. The supported Python versions will be implicitly
converted to package classifiers, so no need to specify them in parameter classifiers
.
The following classifiers are implicitly handled:
- license
The license specified by parameter
license
is translated into a classifier.
See alsopyTooling.Licensing.License.PythonClassifier()
- Python versions
Always add
Programming Language :: Python :: 3 :: Only
.
For each value inpythonVersions
, oneProgramming Language :: Python :: Major.Minor
is added.- Development status
The development status specified by parameter
developmentStatus
is translated to a classifier and added.
See also
Handling of extra requirements
If additional requirement files are provided, e.g. requirements to build the documentation, then extra
requirements are defined. These can be installed via pip install packageName[extraName]
. If so, an extra called
all
is added, so developers can install all dependencies needed for package development.
doc
If parameter
documentationRequirementsFile
is present, an extra requirements calleddoc
will be defined.test
If parameter
unittestRequirementsFile
is present, an extra requirements calledtest
will be defined.build
If parameter
packagingRequirementsFile
is present, an extra requirements calledbuild
will be defined.- User-defined
If parameter
additionalRequirements
is present, an extra requirements for every mapping entry in the dictionary will be added.all
If any of the above was added, an additional extra requirement called
all
will be added, summarizing all extra requirements.
Handling of keywords
If parameter keywords
is not specified, the dunder variable __keywords__
from sourceFileWithVersion
will be used. Otherwise, the content of the parameter, if not None or empty.
DescribePythonPackageHostedOnGitHub
DescribePythonPackageHostedOnGitHub()
is a helper function to describe a Python package when
the source code is hosted on GitHub.
This is a wrapper for DescribePythonPackage()
, because some parameters can be simplified by
knowing the GitHub namespace and repository name: issue tracker URL, source code URL, …
Todo
- normal packages
PackageName
- namespace package root package
NamespacePackage.*
- namespace package sub package
NamespacePackage.PackageName
deriving URLs
Usage in setup.py
from setuptools import setup
from pathlib import Path
from pyTooling.Packaging import DescribePythonPackageHostedOnGitHub
packageName = "pyTooling.Packaging"
setup(
**DescribePythonPackageHostedOnGitHub(
packageName=packageName,
description="A set of helper functions to describe a Python package for setuptools.",
gitHubNamespace="pyTooling",
keywords="Python3 setuptools package wheel installation",
sourceFileWithVersion=Path(f"{packageName.replace('.', '/')}/__init__.py"),
developmentStatus="beta",
pythonVersions=("3.8", "3.9", "3.10")
)
)