Basic functionality =================== The big-picture purpose of astropy-helpers is to provide customization to Python's packaging infrastructure process in ways that the Astropy Project has found to help simplifying the developing and releasing packages. This is primarily built around ``setup.py`` commands, as outlined below, as well as code to help manage version numbers and better control the build process of larger packages. Custom setup.py commands ------------------------ The main part of astropy-helpers is to provide customized setuptools commands. For example, in a package that uses astropy-helpers, the following command will be available:: python setup.py build_docs and this command is implemented in astropy-helpers. To use the custom commands described here, you can use the :func:`~astropy_helpers.setup_helpers.register_commands` function by adding:: from astropy_helpers.setup_helpers import register_commands to your ``setup.py`` file, then do:: cmdclassd = register_commands(NAME, VERSION, RELEASE) where ``NAME`` is the name of your package, ``VERSION`` is the full version string, and ``RELEASE`` is a boolean value indicating whether the version is a stable released version (``True``) or a developer version (``False``). Finally, pass ``cmdclassd`` to the ``setup`` function:: setup(..., cmdclass=cmdclassd) The commands we provide or customize are: python setup.py test ^^^^^^^^^^^^^^^^^^^^ This command will automatically build the package, install it to a temporary directory, and run the tests using `pytest `_ on this installed version. Note that the bulk of this command is actually defined in ``astropy.tests.command.AstropyTest``, because that allows the test machinery to operate outside a setuptools command. This, here we simply define the custom setuptools command. python setup.py sdist ^^^^^^^^^^^^^^^^^^^^^ We redefine ``sdist`` to use the version from distutils rather than from setuptools, as the setuptools version requires duplication of information in ``MANIFEST.in``. python setup.py build_docs ^^^^^^^^^^^^^^^^^^^^^^^^^^ This command will automatically build the package, then run sphinx to build the documentation. This makes development much easier because it ensures sphinx extensions that use the package's code to make documentation are actually using the in-development version of the code. Sphinx itself provides a custom setuptools command, which we expand with the following options: * ``-w``: set the return code to 1 if there are any warnings during the build process. * ``-l``: completely clean previous builds, including files generated by the sphinx-automodapi package (which creates API pages for different functions/classes). * ``-n``: disable the intersphinx option. * ``-o``: open the documentation in a browser if a build finishes successfully. In addition, ``build_docs`` will automatically download and temporarily install sphinx-astropy (which is a meta-package that provides standardized configuration and documentation dependencies for astropy packages) if it isn't already installed. Temporary installation means that the package will be installed into an ``.eggs`` directory in the current working directory, and it will only be available for the duration of the call to ``build_docs``. python setup.py build_ext ^^^^^^^^^^^^^^^^^^^^^^^^^ This is also used when running ``build`` or ``install``. We add several features compared to the default ``build_ext`` command: * For packages with C/Cython extensions, we create a ``packagename._compiler`` submodule that contains information about the compilers used. * Packages that need to build C extensions using the Numpy C API, we allow those packages to define the include path as ``'numpy'`` as opposed to having to import Numpy and call ``get_include``. The goal is to solve the issue that if one has to import Numpy to define extensions, then Numpy has to be installed/available before the package is installed, which means that one needs to install Numpy in a separate installation step. * We detect broken compilers and replace them with other compilers on-the-fly unless the compiler is explicitly specified with the ``CC`` environment variable. * If Cython is not installed, then we automatically check for generated C files (which are normally present in the stable releases) and give a nice error if these are not found. Version helpers --------------- Another piece of functionality we provide in astropy-helpers is the ability to generate a ``packagename.version`` module that includes functions that automatically set the version string for developer versions, to e.g. ``3.2.dev22213`` so that each developer version has a unique number (although note that branches an equal number of commits away from the master branch will share the same version number). In addition, this module contains variables such as ``major``, ``minor``, and ``bugfix``, as well as ``version_info`` (a tuple of the previous three values), a ``release`` flag that indicates whether we are using a stable release, and several other complementary variables. To use the :func:`~astropy_helpers.version_helpers.get_git_devstr`, import:: from astropy_helpers.version_helpers import get_git_devstr in your ``setup.py`` file, and you will then be able to use:: VERSION += get_git_devstr() where ``VERSION`` is a version string without any developer version suffix. We then also provide a function :func:`~astropy_helpers.version_helpers.generate_version_py` that generates a ``version.py`` file inside your package (which can then be imported as ``packagename.version``) that contains variables such as ``major``, ``minor``, and ``bugfix``, as well as ``version_info`` (a tuple of the previous three values), a ``release`` flag that indicates whether we are using a stable release, and several other complementary variables. To use this, import:: from astropy_helpers.version_helpers import generate_version_py in your ``setup.py`` file, and call:: generate_version_py(NAME, VERSION, RELEASE, uses_git=not RELEASE) where ``NAME`` is the name of your package, ``VERSION`` is the full version string (including any developer suffix), ``RELEASE`` indicates whether the version is a stable or developer version, and ``uses_git`` indicates whether we are in a git repository (using ``not RELEASE`` is sensible since git is not available in a stable release). Collecting package information ------------------------------ The ``setup`` function from setuptools can take a number of options that indicate for example what extensions to build, and what package data to include. However, for large packages this can become cumbersome. We therefore provide a mechanism for defining extensions and package data inside individual sub-packages. To do this, you can create ``setup_package.py`` files anywhere in your package, and these files can include one or more of the following functions: * ``get_package_data``: This function, if defined, should return a dictionary mapping the name of the subpackage(s) that need package data to a list of data file paths (possibly including wildcards) relative to the path of the package's source code. e.g. if the source distribution has a needed data file ``astropy/wcs/tests/data/3d_cd.hdr``, this function should return ``{'astropy.wcs.tests':['data/3d_cd.hdr']}``. See the ``package_data`` option of the :func:`distutils.core.setup` function. It is recommended that all such data be in a directory named ``data`` inside the package within which it is supposed to be used. This package data should be accessed via the ``astropy.utils.data.get_pkg_data_filename`` and ``astropy.utils.data.get_pkg_data_fileobj`` functions. * ``get_extensions``: This provides information for building C or Cython extensions. If defined, it should return a list of ``distutils.core.Extension`` objects. * ``get_build_options``: This function allows a package to add extra build options. It should return a list of tuples, where each element has: - *name*: The name of the option as it would appear on the commandline or in the ``setup.cfg`` file. - *doc*: A short doc string for the option, displayed by ``setup.py build --help``. - *is_bool* (optional): When `True`, the option is a boolean option and doesn't have an associated value. Once an option has been added, its value can be looked up using ``astropy_helpers.setup_helpers.get_distutils_build_option``. * ``get_external_libraries``: This function declares that the package uses libraries that are included in the astropy distribution that may also be distributed elsewhere on the users system. It should return a list of library names. For each library, a new build option is created, ``'--use-system-X'`` which allows the user to request to use the system's copy of the library. The package would typically call ``astropy_helpers.setup_helpers.use_system_library`` from its ``get_extensions`` function to determine if the package should use the system library or the included one. * ``get_entry_points()``: This function can returns a dict formatted as required by the ``entry_points`` argument to ``setup()``. With these files in place, you can then make use of the :func:`~astropy_helpers.setup_helpers.get_package_info` function in your ``setup.py`` file with:: from astropy_helpers.setup_helpers import get_package_info ... package_info = get_package_info() ... setup(..., **package_info)