diff --git a/.readthedocs.yml b/.readthedocs.yml index 271aee44..6901e0da 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,4 +1,11 @@ -requirements_file: requirements.txt +version: 2 + +build: + image: latest python: - version: 3.5 + version: 3.7 + install: + - requirements: requirements.txt + +formats: [] diff --git a/.travis-deploy.sh b/.travis-deploy.sh index 51912f7b..152b432a 100644 --- a/.travis-deploy.sh +++ b/.travis-deploy.sh @@ -19,18 +19,6 @@ if [[ "${TRAVIS_PULL_REQUEST}" = "false" && "$TRAVIS_OS_NAME" = "linux" && $TASK git checkout master rsync -avz --delete --exclude .git/ ../packagename/ ./ git add -A - - # Add astropy_helpers manually at the version in the cookiecutter template - git submodule add https://github.com/astropy/astropy-helpers astropy_helpers || true - git submodule update --init - cd astropy_helpers - # parse the json with jq to get the helpers version - helpers_version=$(jq -r ".cookiecutter.astropy_helpers_version" $HOME/.cookiecutter_replay/package-template.json) - git checkout $helpers_version - cp ah_bootstrap.py ../ - cd .. - git add astropy_helpers ah_bootstrap.py - git commit --allow-empty -m "Update rendered version to ""$TRAVIS_COMMIT" git push origin master fi diff --git a/.travis.yml b/.travis.yml index 213526d1..299bcccd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,10 @@ -# We set the language to c because python isn't supported on the MacOS X nodes -# on Travis. However, the language ends up being irrelevant anyway, since we -# install Python ourselves using conda. -language: c +language: python os: - linux +python: 3.8 + env: global: @@ -13,10 +12,6 @@ env: # overridden underneath. They are defined here in order to save having # to repeat them for all configurations. - FOLDERNAME='packagename' - - PYTHON_VERSION=3.7 - - CONDA_DEPENDENCIES='cython astropy sphinx' - - PIP_DEPENDENCIES='cookiecutter gitpython pytest-astropy sphinx-astropy' - - EVENT_TYPE='pull_request push' - TASK='test' - EXTRA_CONTEXT='' - FLAGS='' @@ -27,26 +22,23 @@ env: # being overridden here must exist in the cookiecutter.json # See https://cookiecutter.readthedocs.io/en/0.9.1/advanced_usage.html#injecting-extra-context - EXTRA_CONTEXT='' - - EXTRA_CONTEXT='include_example_cython_code=y' + - EXTRA_CONTEXT='use_compiled_extensions=y include_example_code=y' - EXTRA_CONTEXT="package_name=AstropyProject" FOLDERNAME='AstropyProject' - EXTRA_CONTEXT='_parent_project=sunpy' - - EXTRA_CONTEXT='minimum_python_version=3.5' - - TASK='render' FLAGS='--config-file rendered.yml --no-input' EXTRA_CONTEXT='include_example_cython_code=y initialize_git_repo=n license=Other' + - EXTRA_CONTEXT='minimum_python_version=3.6' + - TASK='render' FLAGS='--config-file rendered.yml --no-input' EXTRA_CONTEXT='use_compiled_extensions=y include_example_code=y license=Other' install: - - - git clone --depth 1 git://github.com/astropy/ci-helpers.git - - source ci-helpers/travis/setup_conda.sh + - pip install sphinx-astropy cookiecutter gitpython tox script: - cd docs ; make html ; cd .. - cookiecutter --no-input ./ -o ../test $EXTRA_CONTEXT $FLAGS - cd ../test/$FOLDERNAME - - if [[ $TASK == 'test' ]]; then python setup.py egg_info; fi - - if [[ $TASK == 'test' ]]; then python setup.py build; fi - - if [[ $TASK == 'test' ]]; then python setup.py build_docs; fi - - if [[ $TASK == 'test' ]]; then python setup.py test; fi - - if [[ $TASK == 'error_check' ]]; then python setup.py egg_info 2>&1 | grep "ERROR:"; fi + - git init + - if [[ $TASK == 'test' ]]; then tox -e py38-test; fi + - if [[ $TASK == 'test' ]]; then tox -e build_docs; fi + - if [[ $TASK == 'test' ]]; then tox -e codestyle; fi after_success: diff --git a/QUESTIONS.rst b/QUESTIONS.rst index c3019d81..8d2b4b52 100644 --- a/QUESTIONS.rst +++ b/QUESTIONS.rst @@ -5,20 +5,21 @@ To use the package template run ``cookiecutter gh:astropy/package-template``, once you have run this command you will be asked a series of questions. Below is a description of each of the prompts. -1. ``package_name``: This is a human readable name for your package, like 'Astropy' or 'SunPy'. -2. ``module_name``: This is the name of your python package i.e. 'astropy' or 'sunpy'. +1. ``package_name``: This is a human readable name for your package, like ``Astropy`` or ``SunPy``. +2. ``module_name``: This is the name of your python package i.e. ``astropy`` or ``sunpy``. 3. ``short_description``: This is a one sentence description of your package. -4. ``long_description``: This is a multi-line description of your package. -5. ``author_name``: The name or names of the authors. -6. ``author_email``: A contact email for the authors. -7. ``license``: The license of your project. -8. ``project_url``: Project website. -9. ``include_example_code``: This includes a set of example python and cython files showing you how to use the package template. If you choose 'n' then none of this will be included and you will have to populate the directory structure before you can import the package. +4. ``author_name``: The name or names of the authors. +5. ``author_email``: A contact email for the authors. +6. ``license``: The license of your package. +7. ``project_url``: Project website. +8. ``include_example_code``: This includes a set of example python files showing you how to use the package template. If you choose ``n`` then none of this will be included and you will have to populate the directory structure before you can import the package. +9. ``use_compiled_extensions``: Whether you plan to use compiled extensions in your package 10. ``include_cextern_folder``: The cextern folder should be used if you are including non-python C code. -11. ``edit_on_github_extension``: Set to "True" to enable the edit on GitHub sphinx extension. +11. ``edit_on_github_extension``: Set to ``True`` to enable the edit on GitHub sphinx extension. 12. ``github_project``: This is the GitHub identifier for the edit on GitHub extension and the changelog link extension. -13. ``project_version``: The current version number of your project. -14. ``use_travis_ci``: If 'y' the template will include an example ``.travis.yml`` file for the Travis CI service. -15. ``use_read_the_docs``: If 'y' the ``read_the_docs.yml`` and ``.rtd-environment.yml`` files will be included for using conda on Read the Docs. -16. ``sphinx_theme``: The value of the ``html_theme`` variable in the sphinx configuration file. -17. ``minimum_python_version``: Version string of minimum supported Python version +13. ``use_travis_ci``: If ``'y'`` the template will include an example ``.travis.yml`` file for the Travis CI service. +14. ``use_read_the_docs``: If ``'y'`` the ``read_the_docs.yml`` and ``.rtd-environment.yml`` files will be included for using conda on Read the Docs. +15. ``sphinx_theme``: The value of the ``html_theme`` variable in the sphinx configuration file. +16. ``required_dependencies``: Comma-separated list of required dependencies +17. ``optional_dependencies``: Comma-separated list of optional dependencies +18. ``minimum_python_version``: Version string of minimum supported Python version diff --git a/TEMPLATE_CHANGES.md b/TEMPLATE_CHANGES.md index 8764f0f0..b46cec66 100644 --- a/TEMPLATE_CHANGES.md +++ b/TEMPLATE_CHANGES.md @@ -15,6 +15,9 @@ be copied over manually if desired. - Removed Appveyor CI option. Use ``os: windows`` in Travis CI. [#420] +- Refactored the template to follow the recommendations in APE 17: + https://github.com/astropy/astropy-APEs/blob/master/APE17.rst [#438] + 2.1 (unreleased) ---------------- diff --git a/cookiecutter.json b/cookiecutter.json index d2dbbc24..b4850f7e 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -2,26 +2,20 @@ "package_name": "packagename", "module_name": "{{ cookiecutter.package_name|lower()|replace(' ', '_') }}", "short_description": "{{ cookiecutter.package_name|replace('-', ' ')|replace('_', ' ') }}", - "long_description": "", "author_name": "Astropy Developers", "author_email": "", "license": ["BSD 3-Clause", "GNU GPL v3+", "Apache Software Licence 2.0", "BSD 2-Clause", "Other"], "project_url": "http://docs.astropy.org/projects/package-template/", - "project_version": "0.0.dev", - "include_example_code": "y", - "include_example_cython_code": "n", "include_cextern_folder": "n", + "use_compiled_extensions": "y", + "include_example_code": "y", "edit_on_github_extension": "False", "github_project": "astropy/astropy", "use_travis_ci": "y", "use_read_the_docs": "y", "sphinx_theme": "astropy-bootstrap", - "initialize_git_repo": "y", "_parent_project": "astropy", - "_install_requires": "astropy", - "_copy_without_render": [ - "docs/_templates" - ], - "minimum_python_version": ["3.7", "3.6", "3.5"], - "astropy_helpers_version": "v3.2.1" + "required_dependencies": "astropy", + "optional_dependencies": "", + "minimum_python_version": ["3.6", "3.7", "3.8"] } diff --git a/docs/ape17.rst b/docs/ape17.rst new file mode 100644 index 00000000..02807a6f --- /dev/null +++ b/docs/ape17.rst @@ -0,0 +1,611 @@ +APE 17 Migration Guide +====================== + +.. warning:: This guide is not yet ready for widespread use and may + still change significantly. + +The Astropy project is now transitioning from using astropy-helpers for +infrastructure to more standard Python packaging tools. The motivation +and implications of this are discussed in an Astropy Proposal for +Enhancements: `APE 17: A roadmap for package infrastructure without +astropy-helpers `__. +The core astropy package has already migrated and these changes will be included +in astropy v4.1. + +This page aims to provide a guide to migrating to using the new infrastructure +described in APE 17. We assume that your package is currently using +astropy-helpers and that you have used a version of the Astropy package-template +in the past to set up your package (though this guide might still be useful if +the latter is not the case). + +Throughout this guide, we will assume that your package is called ``my-package`` +and that the module is called ``my_package``. We deliberately choose a name +where the package name is different from the module name, but for many cases, +these will be the same. + +.. _step-rerender: + +Step 0: Re-rendering the template +--------------------------------- + +In this guide, we will describe the changes to make to the files in your package. +To make some of the steps easier below, you should first re-run cookiecutter to +re-render the template to a temporary folder:: + + cookiecutter gh:astropy/package-template -o my_package_tmp + +We will refer to some of the rendered files in several of the steps below. + +Step 1: Remove astropy-helpers +------------------------------ + +To remove the astropy-helpers submodule, first, run:: + + git rm -r astropy_helpers + +if ``astropy_helpers`` was the only submodule in your repository, the +``.gitmodules`` file will be empty, you can remove this if you wish:: + + git rm -f .gitmodules + +Next you should remove the ``ah_bootstrap.py`` file:: + + git rm ah_bootstrap.py + +You can now commit your changes with:: + + git commit -m "Remove astropy-helpers submodule" + +Step 2: Update/create ``setup.cfg`` +----------------------------------- + +The next step is to update make sure that you have a `setup.cfg +`_ +file that contains meta-data about the package. If you already have this file, +you will likely need to update it, and if you don’t already have this file, you +will need to create it by copying over the ``setup.cfg`` file generated in +:ref:`Step 0 `. If you are updating an existing file, you may +also find it easier to start from the file generated in :ref:`Step 0 ` +and add back any customizations. + +In any case this file should contain at least the following entries:: + + [metadata] + name = my-package + author = ... + author_email = ... + license = ... + license_file = LICENSE.rst + url = ... + description = ... + long_description = file: README.rst + + [options] + zip_safe = False + packages = find: + install_requires = + numpy + astropy + ... + python_requires = >=3.6 + +Replace the ``...`` with the information for your package. Make sure +that ``license_file`` and ``long_description`` have the right filename, +and specify your required dependencies one per line in the +``install_requires`` section. Make sure the ``python_requires`` line is +set to indicate the correct minimum Python version for your package. + +If you already had a file, make sure you remove the following entries +(if present): + +- ``package_name`` +- ``version`` +- ``tests_require`` +- ``[ah_bootstrap]`` and all entries in it + +Also remove any existing ``setup_requires`` entry, though we'll add +back this key for the setuptools-scm package in +:ref:`Step 8 `. + +Step 3 - Define optional, test, and docs dependencies +----------------------------------------------------- + +Next up, add a new section to the ``setup.cfg`` file (or modify, if it +already exists), to specify optional dependencies as well as +dependencies required to run tests and build the documentation, for +example:: + + [options.extras_require] + all = + scipy + matplotlib + test = + pytest-astropy + docs = + sphinx-astropy + +If you don’t need any optional dependencies, remove the ``all`` section. +You will likely need to have at least ``pytest-astropy`` in the ``test`` +section and ``sphinx-astropy`` in ``docs``. + +Step 4 - Define package data +---------------------------- + +If your package includes non-Python data files, you will need to update +how you declare which data files to include. If you have been using the +Astropy package template, it is likely that you have functions called +``get_package_data`` defined inside ``setup_package.py`` files. Remove +these functions, and instead define the package data using a +``[options.package_data]`` section inside your ``setup.cfg`` file, +e.g.:: + + [options.package_data] + * = *.fits, *.csv + my_package.tests = data/* + +In the above example, all ``.fits`` and ``.csv`` in the package will be +included as well as all files inside ``my_package/tests/data``. + +.. _step-setup-py: + +Step 5 - Update your ``setup.py`` file +-------------------------------------- + +Copy the ``setup.py`` file you generated in :ref:`Step 0 ` and replace your existing one +- it should be good to go as-is without any further customizations. + +.. _step-pyproject-toml: + +Step 6: add a ``pyproject.toml`` file +------------------------------------- + +The ``pyproject.toml`` file is used to declare dependencies needed to run +``setup.py`` and build the package. Copy the ``pyproject.toml`` file you +generated in :ref:`Step 0 ` and replace your existing one. + +If your package doesn’t have any compiled extensions, the file should contain:: + + [build-system] + requires = ["setuptools", + "setuptools_scm", + "wheel"] + build-backend = 'setuptools.build_meta' + +Step 7 - Handling C/Cython extensions +------------------------------------- + +If your package has no compiled C/Cython extensions, you can skip this +step. Otherwise, if you have C or Cython extensions, you can either +define your extensions manually inside the ``setup.py`` file or make use +of the `extension-helpers `__ +package to collect extensions in a similar way to astropy-helpers. + +Step 7a - Defining extensions manually +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can define extensions manually as described +`here `__. +If you do this, you can remove all ``setup_package.py`` files in your +package, and you don't need to include extension-helpers in the +``pyproject.toml`` file. + +If you have Cython extensions or your extensions use the NumPy C API, +proceed to :ref:`Step 7c `, otherwise you can proceed to +:ref:`Step 8 `. + +Step 7b - Using extension-helpers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the extension-helpers package to: + +- Automatically define extensions for Cython files. +- Pick up extensions declared in ``setup_package.py`` files, as + described in the `extension-helpers + documentation `__. + +The latter works by looking through all the ``setup_package.py`` files +in your package and executing the ``get_extensions()`` functions, which +each should return a list of extensions. Check through your existing +``setup_package.py`` files (if any), and make sure that any +``astropy_helpers`` imports are changed to ``extension_helpers``. Also +note that all functions in extension-helpers should now be imported from +the top level. See the `extension-helpers API documentation +`_ for a complete +list of functions still provided by extension-helpers. Finally, make +sure that any instance of ``include_dirs='numpy'`` is changed to +``include_dirs=np.get_include()`` and add the ``import numpy as np`` +import if not already present. + +Provided you indicated when you generated the template in :ref:`Step 0 ` +that you wanted to use compiled extensions, you should be good to go. If not, +make sure you add: + +.. code:: python + + from extension_helpers.setup_helpers import get_extensions + +just under the following lines at the top of the ``setup.py`` file: + +.. code:: python + + import sys + from setuptools import setup + +In addition, in the same file, add ``ext_modules=get_extensions()`` to the +call to ``setup.py``. + +If you have Cython extensions or your extensions use the NumPy C API, +proceed to :ref:`Step 7c `, otherwise you can proceed to +:ref:`Step 8 `. + +.. _step-cython-numpy: + +Step 7c - Cython and Numpy build-time dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your compiled extensions rely on the NumPy C API, you will need to +declare Numpy as a build-time dependency in ``pyproject.toml``. Note +that as described in `APE +17 `__, +you need to pin the build-time Numpy dependency to the **oldest** +supported Numpy version for each Python version. However, rather than doing this +manually, you can add the ``oldest-supported-numpy`` package to the build +dependencies in your ``pyproject.toml`` file. In addition if you have Cython +extensions, you will need to also add an entry for Cython, pinning it to a +recent version. Provided you indicated when you generated the template in :ref:`Step 0 ` +that you wanted to use compiled extensions, you should be good to go as both +``oldest-supported-numpy`` and ``cython`` should be in the ``pyproject.toml`` +file. In this case your ``pyproject.toml`` file will look like:: + + [build-system] + requires = ["setuptools", + "setuptools_scm", + "wheel", + "extension-helpers", + "oldest-supported-numpy", + "cython==0.29.14"] + build-backend = 'setuptools.build_meta' + +Whenever a new major Python version is released, you will likely need to +update the Cython pinning to use the most recent Cython version available. + +.. _step-setuptools-scm: + +Step 8 - Using setuptools_scm +----------------------------- + +The `setuptools_scm `__ +package is now recommended to manage the version numbers for your +package. The way this works is that instead of setting the version +number manually in, e.g., ``setup.cfg`` or elsewhere in your package, +the version number is based on git tags. + +In :ref:`Steps 5 ` and :ref:`6 `, we already +added the required entry for setuptools_scm to ``setup.py`` and +``pyproject.toml``. + +In addition to these, we recommend that you define ``setup_requires`` inside the +``[options]`` section of your ``setup.cfg`` file:: + + [options] + ... + setup_requires = setuptools_scm + ... + +This will already be the case if you copied the ``setup.cfg`` generated in +:ref:`Step 0 `. Having ``setup_requires`` is not strictly +necessary but will make it possible for ``python setup.py --version`` to work +without having to install ``setuptools_scm`` manually. + +Next, check your ``.gitignore`` and make sure that you have a line containing:: + + my_package/version.py + +Finally, copy over the ``_astropy_init.py`` file generated in :ref:`Step 0 `, or +alternatively edit your ``my_package/_astropy_init.py`` file and remove the +following lines: + +.. code:: python + + try: + from .version import githash as __githash__ + except ImportError: + __githash__ = '' + +and remove ``'__githash__'`` from the ``__all__`` list at the top of the +file. The git hash is now contained in the version number, so this is no +longer needed. + +Step 9 - Configuring pytest +--------------------------- + +To make sure that pytest works properly, you can set a few options in a +``[tool:pytest]`` section in your ``setup.cfg`` file:: + + [tool:pytest] + testpaths = "my_package" "docs" + astropy_header = true + doctest_plus = enabled + text_file_format = rst + addopts = --doctest-rst + +For the ``testpaths`` line, make sure you replace ``my_package`` with the name +of your package. This section will already exist if you copied the ``setup.cfg`` +generated in :ref:`Step 0 `. + +The remaining options ensure that the output from pytest includes a +header that lists dependencies and system information, and also ensure +that the ``.rst`` files are picked up and tested by pytest. + +Step 10 - Update ``MANIFEST.in`` +-------------------------------- + +Edit your ``MANIFEST.in`` file to remove the following lines, if present +(and any other line related to ``astropy_helpers``) - those lines might +include any of the following:: + + include ez_setup.py + include ah_bootstrap.py + + # the next few stanzas are for astropy_helpers. It's derived from the + # astropy_helpers/MANIFEST.in, but requires additional includes for the actual + # package directory and egg-info. + + include astropy_helpers/README.rst + include astropy_helpers/CHANGES.rst + include astropy_helpers/LICENSE.rst + recursive-include astropy_helpers/licenses * + + include astropy_helpers/ez_setup.py + include astropy_helpers/ah_bootstrap.py + + recursive-include astropy_helpers/astropy_helpers *.py *.pyx *.c *.h + recursive-include astropy_helpers/astropy_helpers.egg-info * + # include the sphinx stuff with "*" because there are css/html/rst/etc. + recursive-include astropy_helpers/astropy_helpers/sphinx * + + prune astropy_helpers/build + prune astropy_helpers/astropy_helpers/tests + +Then add a new line near the top with the following:: + + include pyproject.toml + +Step 11 - Updating your documentation configuration +--------------------------------------------------- + +You will need to edit the ``docs/conf.py`` file to make sure it does not +use astropy-helpers. If you see a code block such as: + +.. code:: python + + try: + import astropy_helpers + except ImportError: + # Building from inside the docs/ directory? + if os.path.basename(os.getcwd()) == 'docs': + a_h_path = os.path.abspath(os.path.join('..', 'astropy_helpers')) + if os.path.isdir(a_h_path): + sys.path.insert(1, a_h_path) + + # Load all of the global Astropy configuration + from astropy_helpers.sphinx.conf import * + + # Get configuration information from setup.cfg + try: + from ConfigParser import ConfigParser + except ImportError: + from configparser import ConfigParser + +you should change it to: + +.. code:: python + + try: + from sphinx_astropy.conf.v1 import * # noqa + except ImportError: + print('ERROR: the documentation requires the sphinx-astropy package to be installed') + sys.exit(1) + + # Get configuration information from setup.cfg + from configparser import ConfigParser + conf = ConfigParser() + +Find and replace any instances of ``package_name`` in the file with +``name``. + +Step 12 - Setting up tox +------------------------ + +`tox `__ is a tool for automating +commands, which is well suited to, e.g., running tests for your package or +building the documentation. One of the benefits of using tox is that it +will (by default) create a source distribution for your package and +install it into a virtual environment before running tests or building +docs, which means that it will be a good test of whether, e.g., you have +declared the package data correctly. + +As a starting point, copy over the ``tox.ini`` file generated in :ref:`Step 0 `. +You can always then customize it if needed (although it should work out +of the box). + +Once you have done this you should be able to do the following: + +Run tests with minimal dependencies:: + + tox -e test + +Run tests with astropy LTS and Numpy 1.16:: + + tox -e test-astropylts-numpy116 + +Run tests with all optional dependencies:: + + tox -e test-alldeps + +Run tests with minimal dependencies and the latest developer version of +numpy and astropy:: + + tox -e test-devdeps + +Build the documentation:: + + tox -e build_docs + +Run code style checks on your code:: + + tox -e codestyle + +The ``{posargs}`` corresponds to arguments passed to ``tox`` after a +``--`` separator - for example to make pytest verbose in a ``test`` +environment, you can do:: + + tox -e test -- -v + +Step 13 - Updating your Continuous Integration +---------------------------------------------- + +This step will depend on what continuous integration services you use. Broadly +speaking, unless there are dependencies you need that can only be installed with +conda, you should no longer need to use ci-helpers to install these. The +recommended approach is to use the tox file to set up the different +configurations you want to use, and to then keep the CI configuration as simple +as possible. + +If you use Travis CI, a good place to start is the ``.travis.yml`` file +generated in :ref:`Step 0 `, and you can then see if any previous customizations you had +made need to be copied over. This file shows how one can configure Travis to use +tox, optionally using conda via ci-helpers to set up Python on MacOS X and +Windows. + +Step 14 - Update ReadTheDocs configuration +------------------------------------------ + +With the set-up described in this migration guide, you should be able to +simplify the configuration for ReadTheDocs. This can be done via a +``readthedocs.yml`` or ``.readthedocs.yml`` file (the latter is recommended). +See the `ReadTheDocs `_ +documentation for more information about this file. + +You should be able to copy over the ``.readthedocs.yml`` file created in :ref:`Step 0 `. +With this updated file, you should now be able to remove any pip requirements +file or conda YAML file that were previously used by ``readthedocs.yml``. + +Step 15 - Coverage configuration +-------------------------------- + +Preivously, astropy-helpers expected the coverage configuration to +be located in ``my_package/tests/coveragerc``. This is now no longer +necessary, so you can now define the coverage configuration inside +the ``setup.cfg`` file, which should help reduce the number of files +to keep track of. Add the following to the bottom of your ``setup.cfg``:: + + [coverage:run] + omit = + my_package/_astropy_init* + my_package/conftest.py + my_package/*setup_package* + my_package/tests/* + my_package/*/tests/* + my_package/extern/* + my_package/version* + */my_package/_astropy_init* + */my_package/conftest.py + */my_package/*setup_package* + */my_package/tests/* + */my_package/*/tests/* + */my_package/extern/* + */my_package/version* + + [coverage:report] + exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + # Don't complain about packages we have installed + except ImportError + # Don't complain if tests don't hit assertions + raise AssertionError + raise NotImplementedError + # Don't complain about script hooks + def main\(.*\): + # Ignore branches that don't pertain to this version of Python + pragma: py{ignore_python_version} + # Don't complain about IPython completion helper + def _ipython_key_completions_ + +Make sure to replace ``my_package`` by your module name. If you had any +customizations in ``coveragerc`` you can include them here, but otherwise the +above should be sufficient and you should now be able to remove the old file:: + + git rm my_package/tests/coveragerc + +Step 16 - conftest.py file updates +---------------------------------- + +For the header in your test runs to be correct with the latest versions of +astropy, you will need to make sure that you update your ``conftest.py`` file as +described in the `pytest-astropy-header instructions +`_. +You can also copy over the file created in :ref:`Step 0 ` and add back any +customizations you had. + +Step 17 - Final cleanup +----------------------- + +Once you’ve made the above changes, you should be able to remove the +following sections from your ``setup.cfg`` file: + +- ``[build_docs]`` +- ``[build_sphinx]`` +- ``[upload_docs]`` + +You should also add ``pip-wheel-metadata`` to your ``.gitignore`` file. + +**Once you are done, if you would like us to help by reviewing your changes, +you can open a pull request to your package and mention @astrofrog or +@Cadair to ask for a review.** + +Note on releasing your package +------------------------------ + +As a result of the changes above, there are some tweaks to the procedure to +follow for releasing your package - see the `latest instructions +`_ +in the astropy documentation. The two main changes are that you no longer need +to manually update the version number in files, instead the version number +is based on the latest git tag, and in addition the source file should be +built using the `pep517 `_ package. + +Note on conda recipes and pyproject.toml +---------------------------------------- + +While not something you can do until you release your updated package, you will +need to take care to update conda recipes (e.g., in conda-forge) for your +package. In particular, since conda ignores ``pyproject.toml`` files, +you will need to make sure that the build dependencies present in ``pyproject.toml`` +are explicitly listed as build dependencies in the conda recipe. For +packages with compiled extensions and Cython, this would look like:: + + build: + - {{ compiler('c') }} + host: + - pip + - python + - setuptools + - setuptools_scm + - numpy + - cython + - extension-helpers + run: + ... + +while for pure-Python packages you will still need to make sure +``setuptools_scm`` is included in the build dependencies:: + + host: + - pip + - python + - setuptools + - setuptools_scm + run: + ... diff --git a/docs/index.rst b/docs/index.rst index ada6814f..eaca5118 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,9 +20,7 @@ Getting Started The Astropy Package template uses the `Cookiecutter `_ project to make it easier to customise the template for your package. To use the package template you need cookiecutter -installed. The package template also optionally makes use of `gitpython -`_ to setup the -``astropy_helpers`` submodule. Depending on how you have Python +installed. Depending on how you have Python installed these packages can be obtained through either pip or conda:: conda install -c conda-forge cookiecutter gitpython @@ -57,18 +55,19 @@ For further customization of your package including setting up testing and documentation, read the :ref:`next-steps` for further information. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 :caption: Contents: options nextsteps updating + ape17 Removal of Python 2 support =========================== -This package template now supports Python 3.5+ versions only. However we +This package template now supports Python 3.6+ versions only. However we will provide critical bug fixes to the ``cookiecutter-2.x`` branch until the end of 2019. We also provide a Python 2 compatible rendered versions of the template in diff --git a/docs/options.rst b/docs/options.rst index 02a3ab96..422cddc3 100644 --- a/docs/options.rst +++ b/docs/options.rst @@ -10,22 +10,21 @@ a description of each of the prompts: #. ``package_name``: This is a human readable name for your package, like ``Astropy`` or ``SunPy``. #. ``module_name``: This is the name of your python package i.e. ``astropy`` or ``sunpy``. #. ``short_description``: This is a one sentence description of your package. -#. ``long_description``: This is a multi-line description of your package. #. ``author_name``: The name or names of the authors. #. ``author_email``: A contact email for the authors. #. ``license``: The license of your package. #. ``project_url``: Project website. -#. ``project_version``: The version number for the package. #. ``include_example_code``: This includes a set of example python files showing you how to use the package template. If you choose ``n`` then none of this will be included and you will have to populate the directory structure before you can import the package. -#. ``include_example_cython_code``: This includes a set of example Cython code, which demonstrates how to add compiled code to the package. +#. ``use_compiled_extensions``: Whether you plan to use compiled extensions in your package #. ``include_cextern_folder``: The cextern folder should be used if you are including non-python C code. #. ``edit_on_github_extension``: Set to ``True`` to enable the edit on GitHub sphinx extension. #. ``github_project``: This is the GitHub identifier for the edit on GitHub extension and the changelog link extension. #. ``use_travis_ci``: If ``'y'`` the template will include an example ``.travis.yml`` file for the Travis CI service. #. ``use_read_the_docs``: If ``'y'`` the ``read_the_docs.yml`` and ``.rtd-environment.yml`` files will be included for using conda on Read the Docs. #. ``sphinx_theme``: The value of the ``html_theme`` variable in the sphinx configuration file. -#. ``initialize_git_repo``: If `gitpython `_ is installed this option will turn the rendered package into a git repository and add and initilize the ``astropy_helpers`` submodule. -#. ``astropy_helpers_version``: The version number of the ``astropy_helpers`` submodule to be used, only used if ``initialize_git_repo`` is true. +#. ``required_dependencies``: Comma-separated list of required dependencies +#. ``optional_dependencies``: Comma-separated list of optional dependencies +#. ``minimum_python_version``: Version string of minimum supported Python version Once the project has been set up, any of the values can still be `manually updated `_. diff --git a/docs/updating.rst b/docs/updating.rst index b7fbbc4f..f1a5ef80 100644 --- a/docs/updating.rst +++ b/docs/updating.rst @@ -2,10 +2,9 @@ Updating Your Package ===================== Once you have setup your project there will come a time when some things in the -package template change, new versions of ``astropy_helpers`` or updates to the -infrastructure you want to utilise. Updating your package with these changes is -a balance between maintaining parity with the original template and customising -your project to your needs. +package template change. Updating your package with these changes is a balance +between maintaining parity with the original template and customising your +project to your needs. Using Cookiecutter to Update @@ -28,10 +27,6 @@ settings configured to your package and then pull the changes into your package. $ cookiecutter [--replay] gh:astropy/package-template -o /tmp - .. note:: - - You should choose 'n' for the "initialize_git_repo" question. - #. **Make a new branch**:: $ git checkout -b update_template diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index f52adecc..ee59f10b 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -47,47 +47,10 @@ def process_licence(licence_name): remove_file('.rtd-environment.yml') remove_file('readthedocs.yml') - if '{{ cookiecutter.include_cextern_folder }}' != 'y': - remove_dir("cextern") - if '{{ cookiecutter.include_example_code }}' != 'y': remove_dir('{{ cookiecutter.module_name }}/example_subpkg/') remove_file('{{ cookiecutter.module_name }}/example_mod.py') remove_file('{{ cookiecutter.module_name }}/tests/test_example.py') - if '{{ cookiecutter.include_example_cython_code }}' != 'y': + if '{{ cookiecutter.use_compiled_extensions }}' != 'y' or '{{ cookiecutter.include_example_code }}' != 'y': remove_file('{{ cookiecutter.module_name }}/example_c.pyx') - - if '{{ cookiecutter.initialize_git_repo }}' == 'y': - try: - from git import Repo - - new_repo = Repo.init(PROJECT_DIRECTORY) - new_repo.git.add('.') - new_repo.index.commit( - "Creation of {{ cookiecutter.package_name }} from astropy package template" - ) - - if '{{ cookiecutter.astropy_helpers_version }}': - Repo.create_submodule( - new_repo, "astropy_helpers", "astropy_helpers", - "https://github.com/astropy/astropy-helpers.git", - "{{ cookiecutter.astropy_helpers_version }}") - new_repo.submodules[0].update() - copy_file('astropy_helpers/ah_bootstrap.py', 'ah_bootstrap.py') - new_repo.git.add('ah_bootstrap.py') - new_repo.index.commit( - "Initialize astropy_helpers at version {{ cookiecutter.astropy_helpers_version }}" - ) - - except ImportError: - print( - "gitpython is not installed so the repository will not be initialised " - "and astropy_helpers not downloaded.") - - else: - urllib.request.urlretrieve( - url=('https://raw.githubusercontent.com/astropy/astropy-helpers/' - '{{ cookiecutter.astropy_helpers_version }}/ah_bootstrap.py'), - filename='ah_bootstrap.py') - urllib.request.urlcleanup() diff --git a/rendered.yml b/rendered.yml index 51ebdc63..08845f29 100644 --- a/rendered.yml +++ b/rendered.yml @@ -2,19 +2,19 @@ default_context: package_name: "packagename" module_name: "packagename" short_description: "Astropy Package Template" - long_description: "This is a rendered version of the cookiecutter Astropy package template, which you can find `here `_. You can also read the `documentation `_ for more details." author_name: "Astropy Developers" author_email: "" license: "BSD 3-Clause" project_url: "http://docs.astropy.org/projects/package-template/" project_version: "0.0.dev" include_example_code: "y" - include_example_cython_code: "y" + use_compiled_extensions: "y" include_cextern_folder: "y" edit_on_github_extension: "False" github_project: "astropy/astropy" use_travis_ci: "y" use_read_the_docs: "y" sphinx_theme: "astropy-bootstrap" - initialize_git_repo: "n" minimum_python_version: "3.6" + required_dependencies: "astropy" + optional_dependencies: "" diff --git a/requirements.txt b/requirements.txt index eaaced99..202ff07d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -astropy-helpers sphinx gitpython sphinx-astropy diff --git a/{{ cookiecutter.package_name }}/.gitignore b/{{ cookiecutter.package_name }}/.gitignore index 55e2af05..7c60f757 100644 --- a/{{ cookiecutter.package_name }}/.gitignore +++ b/{{ cookiecutter.package_name }}/.gitignore @@ -55,6 +55,7 @@ distribute-*.tar.gz .project .pydevproject .settings +pip-wheel-metadata/ # Mac OSX .DS_Store diff --git a/{{ cookiecutter.package_name }}/.readthedocs.yml b/{{ cookiecutter.package_name }}/.readthedocs.yml index 47fe6adf..91be6211 100644 --- a/{{ cookiecutter.package_name }}/.readthedocs.yml +++ b/{{ cookiecutter.package_name }}/.readthedocs.yml @@ -1,5 +1,15 @@ -conda: - file: .rtd-environment.yml +version: 2 + +build: + image: latest python: - setup_py_install: true + version: 3.7 + install: + - method: pip + path: . + extra_requirements: + - docs + - all + +formats: [] diff --git a/{{ cookiecutter.package_name }}/.rtd-environment.yml b/{{ cookiecutter.package_name }}/.rtd-environment.yml deleted file mode 100644 index b046d9de..00000000 --- a/{{ cookiecutter.package_name }}/.rtd-environment.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: {{ cookiecutter.module_name }} - -channels: - - astropy - -dependencies: - - python>={{ cookiecutter.minimum_python_version }} - - astropy - - Cython - - matplotlib - - numpy - - sphinx-astropy -# - pip: -# - dependency_from_pip diff --git a/{{ cookiecutter.package_name }}/.travis.yml b/{{ cookiecutter.package_name }}/.travis.yml index b3726ae4..06cd246b 100644 --- a/{{ cookiecutter.package_name }}/.travis.yml +++ b/{{ cookiecutter.package_name }}/.travis.yml @@ -1,7 +1,8 @@ -# We set the language to c because python isn't supported on the MacOS X nodes -# on Travis. However, the language ends up being irrelevant anyway, since we -# install Python ourselves using conda. -language: c +language: python + +# We need a full clone to make sure setuptools_scm works properly +git: + depth: false os: - linux @@ -17,15 +18,11 @@ addons: - graphviz -stage: Comprehensive tests - stages: # Do the style check and a single test job, don't proceed if it fails - name: Initial tests # Test docs, astropy dev, and without optional dependencies - name: Comprehensive tests - # Slow tests that should only run if comprehensive ones passed - - name: Slow tests # These will only run when cron is opted in - name: Cron tests if: type = cron @@ -33,47 +30,23 @@ stages: env: global: + # The following versions are the 'default' for tests, unless # overridden underneath. They are defined here in order to save having # to repeat them for all configurations. - - PYTHON_VERSION=3.7 - - NUMPY_VERSION=stable - - ASTROPY_VERSION=stable - - MAIN_CMD='python setup.py' - - SETUP_CMD='test' - - EVENT_TYPE='pull_request push' - - {% if cookiecutter.include_example_cython_code == 'y' %} - # For this package-template, we include examples of Cython modules, - # so Cython is required for testing. If your package does not include - # Cython code, you can set CONDA_DEPENDENCIES='' - - CONDA_DEPENDENCIES='Cython' - - CONDA_DEPENDENCIES_DOC='Cython sphinx-astropy' - {% else %} - # List runtime dependencies for the package that are available as conda - # packages here. - - CONDA_DEPENDENCIES='' - - CONDA_DEPENDENCIES_DOC='sphinx-astropy' - {% endif %} - # List other runtime dependencies for the package that are available as - # pip packages here. - - PIP_DEPENDENCIES='' - - # Conda packages for affiliated packages are hosted in channel - # "astropy" while builds for astropy LTS with recent numpy versions - # are in astropy-ci-extras. If your package uses either of these, - # add the channels to CONDA_CHANNELS along with any other channels - # you want to use. - - CONDA_CHANNELS='astropy' - - # If there are matplotlib or other GUI tests, uncomment the following - # line to use the X virtual framebuffer. - # - SETUP_XVFB=True - - # If you want to ignore certain flake8 errors, you can list them - # in FLAKE8_OPT, for example: - # - FLAKE8_OPT='--ignore=E501' - - FLAKE8_OPT='' + + # The following three variables are for tox. TOXENV is a standard + # variable that tox uses to determine the environment to run, + # TOXARGS are arguments passed to tox, and TOXPOSARGS are arguments + # that tox passes through to the {posargs} indicator in tox.ini. + # The latter can be used for example to pass arguments to pytest. + - TOXENV='test' + - TOXARGS='-v' + - TOXPOSARGS='' + + # The following is needed to avoid issues if e.g. Matplotlib tries + # to open a GUI window. + - SETUP_XVFB=True matrix: @@ -81,98 +54,108 @@ matrix: fast_finish: true include: - # Make sure that egg_info works without dependencies - - stage: Initial tests - env: PYTHON_VERSION=3.7 SETUP_CMD='egg_info' # Try MacOS X, usually enough only to run from cron as hardly there are - # issues that are not picked up by a linux worker + # issues that are not picked up by a linux worker. We set language to + # 'c' since 'python' doesn't work on non-Linux platforms. - os: osx + language: c + name: Python 3.7 with required dependencies stage: Cron tests - env: SETUP_CMD='test' EVENT_TYPE='cron' + env: PYTHON_VERSION=3.7 TOXENV=py37-test - # Do a coverage test. + # Do a regular build on Linux with Python 3.8, with cov + # For Linux we use language: python to avoid using conda. - os: linux + python: 3.8 + name: Python 3.8 with required dependencies and measure coverage stage: Initial tests - env: SETUP_CMD='test --coverage' + env: TOXENV=py38-test-cov - # Check for sphinx doc build warnings - we do this first because it - # may run for a long time + # Check for sphinx doc build warnings - os: linux - env: SETUP_CMD='build_docs -w' - CONDA_DEPENDENCIES=$CONDA_DEPENDENCIES_DOC + python: 3.8 + name: Documentation build + stage: Comprehensive tests + env: TOXENV=build_docs - # Now try Astropy dev with the latest Python and LTS with and 3.x. + # Now try Astropy dev with the latest Python - os: linux - env: ASTROPY_VERSION=development - EVENT_TYPE='pull_request push cron' + python: 3.8 + name: Python 3.8 with developer version of astropy + stage: Comprehensive tests + env: TOXENV=py38-test-devdeps + + # And with an older Python, Astropy LTS, and the oldest supported Numpy - os: linux - env: PYTHON_VERSION=3.6 ASTROPY_VERSION=lts NUMPY_VERSION=1.13 + python: 3.6 + name: Python 3.6 astropy LTS and Numpy 1.16 + stage: Comprehensive tests + env: TOXENV=py36-test-astropylts-numpy116 # Add a job that runs from cron only and tests against astropy dev and # numpy dev to give a change for early discovery of issues and feedback # for both developer teams. - os: linux + python: 3.8 + name: Python 3.8 latest developer version of key dependencies stage: Cron tests - env: ASTROPY_VERSION=development NUMPY_VERSION=development - EVENT_TYPE='cron' + env: TOXENV=py38-test-devdeps # Try on Windows. - os: windows - stage: Slow tests - env: SETUP_CMD='test' + language: c + name: Python 3.8 with required dependencies + stage: Comprehensive tests + env: PYTHON_VERSION=3.8 TOXENV=py38-test - # Try all python versions and Numpy versions. Since we can assume that + # Try other python versions and Numpy versions. Since we can assume that # the Numpy developers have taken care of testing Numpy with different # versions of Python, we can vary Python and Numpy versions at the same # time. - os: linux - env: PYTHON_VERSION=3.6 NUMPY_VERSION=1.14 - - os: linux - env: NUMPY_VERSION=1.15 + python: 3.7 + name: Python 3.7 with astropy 3.0 and Numpy 1.17 + stage: Comprehensive tests + env: TOXENV=py37-test-astropy30-numpy117 - # Do a PEP8 test with flake8 + # Do a code style check - os: linux + python: 3.8 + name: Code style checks stage: Initial tests - env: MAIN_CMD='flake8 {{ cookiecutter.module_name }} --count --show-source --statistics $FLAKE8_OPT' SETUP_CMD='' + env: TOXENV=codestyle allow_failures: # Do a PEP8 test with flake8 # (do allow to fail unless your code completely compliant) # - os: linux + # python: 3.8 + # name: Code style checks # stage: Initial tests - # env: MAIN_CMD='flake8 {{ cookiecutter.module_name }} --count --show-source --statistics $FLAKE8_OPT' SETUP_CMD='' + # env: TOXENV=codestyle install: - # We now use the ci-helpers package to set up our testing environment. - # This is done by using Miniconda and then using conda and pip to install - # dependencies. Which dependencies are installed using conda and pip is - # determined by the CONDA_DEPENDENCIES and PIP_DEPENDENCIES variables, - # which should be space-delimited lists of package names. See the README - # in https://github.com/astropy/ci-helpers for information about the full - # list of environment variables that can be used to customize your - # environment. In some cases, ci-helpers may not offer enough flexibility - # in how to install a package, in which case you can have additional - # commands in the install: section below. - - - git clone --depth 1 git://github.com/astropy/ci-helpers.git - - source ci-helpers/travis/setup_conda.sh - - # As described above, using ci-helpers, you should be able to set up an - # environment with dependencies installed using conda and pip, but in some - # cases this may not provide enough flexibility in how to install a - # specific dependency (and it will not be able to install non-Python - # dependencies). Therefore, you can also include commands below (as - # well as at the start of the install section or in the before_install - # section if they are needed before setting up conda) to install any - # other dependencies. + # We now use the ci-helpers package to set up our Python environment + # on Windows and MacOS X but we don't set up any other dependencies, + # instead using tox to do this. See https://github.com/astropy/ci-helpers + # for more information about ci-helpers. + + - if [[ $TRAVIS_OS_NAME != linux ]]; then + git clone --depth 1 git://github.com/astropy/ci-helpers.git; + source ci-helpers/travis/setup_conda.sh; + fi script: - - $MAIN_CMD $SETUP_CMD + - pip install tox + - tox $TOXARGS -- $TOXPOSARGS after_success: - # If coveralls.io is set up for this package, uncomment the line below. - # The coveragerc file may be customized as needed for your package. - # - if [[ $SETUP_CMD == *coverage* ]]; then coveralls --rcfile='{{ cookiecutter.module_name }}/tests/coveragerc'; fi + # If coveralls.io is set up for this package, uncomment the two lines below. + # pip install coveralls + # coveralls + # If codecov is set up for this package, uncomment the two lines below + # pip install codecov + # codecov diff --git a/{{ cookiecutter.package_name }}/MANIFEST.in b/{{ cookiecutter.package_name }}/MANIFEST.in index 71c0fc34..446d6b4b 100644 --- a/{{ cookiecutter.package_name }}/MANIFEST.in +++ b/{{ cookiecutter.package_name }}/MANIFEST.in @@ -1,9 +1,8 @@ include README.rst include CHANGES.rst - -include ah_bootstrap.py include setup.cfg -include {{ cookiecutter.module_name }}/tests/coveragerc +include LICENSE.rst +include pyproject.toml recursive-include {{ cookiecutter.module_name }} *.pyx *.c *.pxd recursive-include docs * @@ -15,25 +14,4 @@ prune build prune docs/_build prune docs/api - -# the next few stanzas are for astropy_helpers. It's derived from the -# astropy_helpers/MANIFEST.in, but requires additional includes for the actual -# package directory and egg-info. - -include astropy_helpers/README.rst -include astropy_helpers/CHANGES.rst -include astropy_helpers/LICENSE.rst -recursive-include astropy_helpers/licenses * - -include astropy_helpers/ah_bootstrap.py - -recursive-include astropy_helpers/astropy_helpers *.py *.pyx *.c *.h *.rst -recursive-include astropy_helpers/astropy_helpers.egg-info * -# include the sphinx stuff with "*" because there are css/html/rst/etc. -recursive-include astropy_helpers/astropy_helpers/sphinx * - -prune astropy_helpers/build -prune astropy_helpers/astropy_helpers/tests - - global-exclude *.pyc *.o diff --git a/{{ cookiecutter.package_name }}/README.rst b/{{ cookiecutter.package_name }}/README.rst index 36c09c86..ade8df96 100644 --- a/{{ cookiecutter.package_name }}/README.rst +++ b/{{ cookiecutter.package_name }}/README.rst @@ -5,8 +5,6 @@ :target: http://www.astropy.org :alt: Powered by Astropy Badge -{{ cookiecutter.long_description|wordwrap(break_long_words=False) }} - License ------- diff --git a/{{ cookiecutter.package_name }}/docs/_templates/autosummary/base.rst b/{{ cookiecutter.package_name }}/docs/_templates/autosummary/base.rst deleted file mode 100644 index 9cabaf52..00000000 --- a/{{ cookiecutter.package_name }}/docs/_templates/autosummary/base.rst +++ /dev/null @@ -1,2 +0,0 @@ -{% extends "autosummary_core/base.rst" %} -{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} \ No newline at end of file diff --git a/{{ cookiecutter.package_name }}/docs/_templates/autosummary/class.rst b/{{ cookiecutter.package_name }}/docs/_templates/autosummary/class.rst deleted file mode 100644 index 6b214a5c..00000000 --- a/{{ cookiecutter.package_name }}/docs/_templates/autosummary/class.rst +++ /dev/null @@ -1,2 +0,0 @@ -{% extends "autosummary_core/class.rst" %} -{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} \ No newline at end of file diff --git a/{{ cookiecutter.package_name }}/docs/_templates/autosummary/module.rst b/{{ cookiecutter.package_name }}/docs/_templates/autosummary/module.rst deleted file mode 100644 index f38315b2..00000000 --- a/{{ cookiecutter.package_name }}/docs/_templates/autosummary/module.rst +++ /dev/null @@ -1,2 +0,0 @@ -{% extends "autosummary_core/module.rst" %} -{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} \ No newline at end of file diff --git a/{{ cookiecutter.package_name }}/docs/conf.py b/{{ cookiecutter.package_name }}/docs/conf.py index 16d1e353..a3e9af02 100644 --- a/{{ cookiecutter.package_name }}/docs/conf.py +++ b/{{ cookiecutter.package_name }}/docs/conf.py @@ -156,15 +156,12 @@ # -- Options for the edit_on_github extension --------------------------------- -if eval(setup_cfg.get('edit_on_github')): +if setup_cfg.get('edit_on_github').lower() == 'true': + extensions += ['sphinx_astropy.ext.edit_on_github'] - versionmod = import_module(setup_cfg['name'] + '.version') edit_on_github_project = setup_cfg['github_project'] - if versionmod.release: - edit_on_github_branch = "v" + versionmod.version - else: - edit_on_github_branch = "master" + edit_on_github_branch = "master" edit_on_github_source_root = "" edit_on_github_doc_root = "docs" diff --git a/{{ cookiecutter.package_name }}/docs/index.rst b/{{ cookiecutter.package_name }}/docs/index.rst index ccaed6a4..9cbaa3a6 100644 --- a/{{ cookiecutter.package_name }}/docs/index.rst +++ b/{{ cookiecutter.package_name }}/docs/index.rst @@ -2,7 +2,6 @@ Documentation ============= This is the documentation for {{ cookiecutter.package_name }}. -{{ cookiecutter.long_description|wordwrap(break_long_words=False) }} .. toctree:: :maxdepth: 2 diff --git a/{{ cookiecutter.package_name }}/pyproject.toml b/{{ cookiecutter.package_name }}/pyproject.toml new file mode 100644 index 00000000..64816ac2 --- /dev/null +++ b/{{ cookiecutter.package_name }}/pyproject.toml @@ -0,0 +1,14 @@ +[build-system] +{% if cookiecutter.include_example_code == 'y' %} +requires = ["setuptools", + "setuptools_scm", + "wheel", + "extension-helpers", + "oldest-supported-numpy", + "cython==0.29.14"] +{% else %} +requires = ["setuptools", + "setuptools_scm", + "wheel"] +{% endif %} +build-backend = 'setuptools.build_meta' diff --git a/{{ cookiecutter.package_name }}/setup.cfg b/{{ cookiecutter.package_name }}/setup.cfg index 059596dd..64bf9224 100644 --- a/{{ cookiecutter.package_name }}/setup.cfg +++ b/{{ cookiecutter.package_name }}/setup.cfg @@ -1,62 +1,83 @@ [metadata] name = {{ cookiecutter.module_name }} -# version should be PEP440 compatible (https://www.python.org/dev/peps/pep-0440/) -version = {{ cookiecutter.project_version }} author = {{ cookiecutter.author_name }} author_email = {{ cookiecutter.author_email }} -description = {{ cookiecutter.short_description }} -long_description = {{ cookiecutter.long_description }} license = {{ cookiecutter.license }} +license_file = licenses/LICENSE.rst url = {{ cookiecutter.project_url }} +description = {{ cookiecutter.short_description }} +long_description = file: README.rst +long_description_content_type = text/x-rst edit_on_github = {{ cookiecutter.edit_on_github_extension }} github_project = {{ cookiecutter.github_project }} -python_requires = ">={{ cookiecutter.minimum_python_version }}" [options] -# install_requires should be formatted as a semicolon-separated list, e.g.: -# install_requires = astropy; scipy; matplotlib -install_requires = {{ cookiecutter._install_requires }} zip_safe = False -use_2to3 = False +packages = find: +python_requires = >={{ cookiecutter.minimum_python_version }} +setup_requires = setuptools_scm +{% if cookiecutter.required_dependencies -%} +install_requires = +{%- for req in cookiecutter.required_dependencies.split(',') %} + {{ req.strip() }}{% endfor %} +{% endif -%} +{% if cookiecutter.include_example_code == 'y' %} [options.entry_points] console_scripts = - {% if cookiecutter.include_example_code == 'y' %} astropy-package-template-example = packagename.example_mod:main - {% else %} - # astropy-package-template-example = packagename.example_mod:main - {% endif %} +{%- endif %} + +[options.extras_require] +{% if cookiecutter.optional_dependencies -%} +all = +{%- for req in cookiecutter.optional_dependencies.split(',') %} + {{ req.strip() }}{% endfor %} +{% endif -%} +test = + pytest-astropy +docs = + sphinx-astropy [options.package_data] -* = *.c {{ cookiecutter.module_name }} = data/* -{{ cookiecutter.module_name }}.tests = coveragerc - -[build_sphinx] -source-dir = docs -build-dir = docs/_build -all_files = 1 - -[build_docs] -source-dir = docs -build-dir = docs/_build -all_files = 1 - -[upload_docs] -upload-dir = docs/_build/html -show-response = 1 [tool:pytest] -minversion = 3.0 -norecursedirs = build docs/_build +testpaths = "{{ cookiecutter.module_name }}" "docs" +astropy_header = true doctest_plus = enabled -addopts = -p no:warnings - -[ah_bootstrap] -auto_use = True +text_file_format = rst +addopts = --doctest-rst -[flake8] -exclude = extern,sphinx,*parsetab.py,astropy_helpers,ah_bootstrap.py,conftest.py,docs/conf.py,setup.py +[coverage:run] +omit = + {{ cookiecutter.module_name }}/_{{ cookiecutter._parent_project }}_init* + {{ cookiecutter.module_name }}/conftest.py + {{ cookiecutter.module_name }}/*setup_package* + {{ cookiecutter.module_name }}/tests/* + {{ cookiecutter.module_name }}/*/tests/* + {{ cookiecutter.module_name }}/extern/* + {{ cookiecutter.module_name }}/version* + */{{ cookiecutter.module_name }}/_{{ cookiecutter._parent_project }}_init* + */{{ cookiecutter.module_name }}/conftest.py + */{{ cookiecutter.module_name }}/*setup_package* + */{{ cookiecutter.module_name }}/tests/* + */{{ cookiecutter.module_name }}/*/tests/* + */{{ cookiecutter.module_name }}/extern/* + */{{ cookiecutter.module_name }}/version* -[pycodestyle] -exclude = extern,sphinx,*parsetab.py,astropy_helpers,ah_bootstrap.py,conftest.py,docs/conf.py,setup.py +[coverage:report] +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + # Don't complain about packages we have installed + except ImportError + # Don't complain if tests don't hit assertions + raise AssertionError + raise NotImplementedError + # Don't complain about script hooks + def main\(.*\): + # Ignore branches that don't pertain to this version of Python + pragma: py{ignore_python_version} + # Don't complain about IPython completion helper + def _ipython_key_completions_ diff --git a/{{ cookiecutter.package_name }}/setup.py b/{{ cookiecutter.package_name }}/setup.py old mode 100755 new mode 100644 index c7715256..11a397b6 --- a/{{ cookiecutter.package_name }}/setup.py +++ b/{{ cookiecutter.package_name }}/setup.py @@ -1,33 +1,85 @@ #!/usr/bin/env python - # Licensed under a 3-clause BSD style license - see LICENSE.rst -import builtins +# NOTE: The configuration for the package, including the name, version, and +# other information are set in the setup.cfg file. -# Ensure that astropy-helpers is available -import ah_bootstrap # noqa +import os +import sys from setuptools import setup from setuptools.config import read_configuration +{% if cookiecutter.use_compiled_extensions == 'y' %} +from extension_helpers import get_extensions +{% endif %} + +# First provide helpful messages if contributors try and run legacy commands +# for tests or docs. + +TEST_HELP = """ +Note: running tests is no longer done using 'python setup.py test'. Instead +you will need to run: + + tox -e test + +If you don't already have tox installed, you can install it with: + + pip install tox + +If you only want to run part of the test suite, you can also use pytest +directly with:: + + pip install -e .[test] + pytest + +For more information, see: + + http://docs.astropy.org/en/latest/development/testguide.html#running-tests +""" + +if 'test' in sys.argv: + print(TEST_HELP) + sys.exit(1) + +DOCS_HELP = """ +Note: building the documentation is no longer done using +'python setup.py build_docs'. Instead you will need to run: + + tox -e build_docs + +If you don't already have tox installed, you can install it with: + + pip install tox + +You can also build the documentation with Sphinx directly using:: -from astropy_helpers.setup_helpers import register_commands, get_package_info -from astropy_helpers.version_helpers import generate_version_py + pip install -e .[docs] + cd docs + make html -# Store the package name in a built-in variable so it's easy -# to get from other parts of the setup infrastructure -builtins._ASTROPY_PACKAGE_NAME_ = read_configuration('setup.cfg')['metadata']['name'] +For more information, see: -# Create a dictionary with setup command overrides. Note that this gets -# information about the package (name and version) from the setup.cfg file. -cmdclass = register_commands() + http://docs.astropy.org/en/latest/install.html#builddocs +""" -# Freeze build information in version.py. Note that this gets information -# about the package (name and version) from the setup.cfg file. -version = generate_version_py() +if 'build_docs' in sys.argv or 'build_sphinx' in sys.argv: + print(DOCS_HELP) + sys.exit(1) -# Get configuration information from all of the various subpackages. -# See the docstring for setup_helpers.update_package_files for more -# details. -package_info = get_package_info() +VERSION_TEMPLATE = """ +# Note that we need to fall back to the hard-coded version if either +# setuptools_scm can't be imported or setuptools_scm can't determine the +# version, so we catch the generic 'Exception'. +try: + from setuptools_scm import get_version + version = get_version(root='..', relative_to=__file__) +except Exception: + version = '{version}' +""".lstrip() -setup(version=version, cmdclass=cmdclass, **package_info) +setup(use_scm_version={'write_to': os.path.join('{{ cookiecutter.module_name }}', 'version.py'), + 'write_to_template': VERSION_TEMPLATE} +{%- if cookiecutter.use_compiled_extensions == 'y' %}, + ext_modules=get_extensions()) +{%- else %}) +{%- endif %} diff --git a/{{ cookiecutter.package_name }}/tox.ini b/{{ cookiecutter.package_name }}/tox.ini new file mode 100644 index 00000000..7ff04353 --- /dev/null +++ b/{{ cookiecutter.package_name }}/tox.ini @@ -0,0 +1,88 @@ +[tox] +envlist = + py{36,37,38}-test{,-alldeps,-devdeps}{,-cov} + py{36,37,38}-test-numpy{116,117,118} + py{36,37,38}-test-astropy{30,40,lts} + build_docs + codestyle +requires = + setuptools >= 30.3.0 + pip >= 19.3.1 +isolated_build = true + +[testenv] + +# Pass through the following environemnt variables which may be needed for the CI +passenv = HOME WINDIR LC_ALL LC_CTYPE CC CI TRAVIS + +# Run the tests in a temporary directory to make sure that we don't import +# this package from the source tree +changedir = .tmp/{envname} + +# tox environments are constructued with so-called 'factors' (or terms) +# separated by hyphens, e.g. test-devdeps-cov. Lines below starting with factor: +# will only take effect if that factor is included in the environment name. To +# see a list of example environments that can be run, along with a description, +# run: +# +# tox -l -v +# +description = + run tests + alldeps: with all optional dependencies + devdeps: with the latest developer version of key dependencies + oldestdeps: with the oldest supported version of key dependencies + cov: and test coverage + numpy116: with numpy 1.16.* + numpy117: with numpy 1.17.* + numpy118: with numpy 1.18.* + astropy30: with astropy 3.0.* + astropy40: with astropy 4.0.* + astropylts: with the latest astropy LTS + +# The following provides some specific pinnings for key packages +deps = + + numpy116: numpy==1.16.* + numpy117: numpy==1.17.* + numpy118: numpy==1.18.* + + astropy30: astropy==3.0.* + astropy40: astropy==4.0.* + astropylts: astropy==4.0.* + + devdeps: git+https://github.com/numpy/numpy.git#egg=numpy + devdeps: git+https://github.com/astropy/astropy.git#egg=astropy + +# The following indicates which extras_require from setup.cfg will be installed +extras = + test + alldeps: all + +commands = + pip freeze + !cov: pytest --pyargs {{ cookiecutter.module_name }} {toxinidir}/docs {posargs} + cov: pytest --pyargs {{ cookiecutter.module_name }} {toxinidir}/docs --cov {{ cookiecutter.module_name }} --cov-config={toxinidir}/setup.cfg {posargs} + +[testenv:build_docs] +changedir = docs +description = invoke sphinx-build to build the HTML docs +extras = docs +commands = + pip freeze + sphinx-build -W -b html . _build/html + +[testenv:linkcheck] +changedir = docs +description = check the links in the HTML docs +extras = docs +commands = + pip freeze + sphinx-build -W -b linkcheck . _build/html + +[testenv:codestyle] +skip_install = true +changedir = . +description = check code style, e.g. with flake8 +deps = flake8 +commands = flake8 {{ cookiecutter.module_name }} --count --max-line-length=100 diff --git a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/__init__.py b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/__init__.py index cab415f6..b69d7423 100644 --- a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/__init__.py +++ b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/__init__.py @@ -24,14 +24,10 @@ class UnsupportedPythonError(Exception): raise UnsupportedPythonError("{{ cookiecutter.module_name }} does not support Python < {}" .format(__minimum_python_version__)) -if not _ASTROPY_SETUP_: # noqa - # For egg_info test builds to pass, put package imports here. -{%- if cookiecutter.include_example_code != 'y' %} - pass -{%- else %} - from .example_mod import * # noqa - # Then you can be explicit to control what ends up in the namespace, - __all__ += ['do_primes'] # noqa - # or you can keep everything from the subpackage with the following instead - # __all__ += example_mod.__all__ +{%- if cookiecutter.include_example_code == 'y' %} +from .example_mod import * # noqa +# Then you can be explicit to control what ends up in the namespace, +__all__ += ['do_primes'] # noqa +# or you can keep everything from the subpackage with the following instead +# __all__ += example_mod.__all__ {%- endif %} diff --git a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/_{{ cookiecutter._parent_project }}_init.py b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/_{{ cookiecutter._parent_project }}_init.py index 7c3fffa6..2dffe8fd 100644 --- a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/_{{ cookiecutter._parent_project }}_init.py +++ b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/_{{ cookiecutter._parent_project }}_init.py @@ -1,6 +1,6 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst -__all__ = ['__version__', '__githash__'] +__all__ = ['__version__'] # this indicates whether or not we are in the package's setup.py try: @@ -13,10 +13,6 @@ from .version import version as __version__ except ImportError: __version__ = '' -try: - from .version import githash as __githash__ -except ImportError: - __githash__ = '' if not _ASTROPY_SETUP_: # noqa diff --git a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/conftest.py b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/conftest.py index 6cbbb303..672b2733 100644 --- a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/conftest.py +++ b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/conftest.py @@ -1,49 +1,49 @@ # This file is used to configure the behavior of pytest when using the Astropy -# test infrastructure. +# test infrastructure. It needs to live inside the package in order for it to +# get picked up when running the tests inside an interpreter using +# packagename.test + import os from astropy.version import version as astropy_version + +# For Astropy 3.0 and later, we can use the standalone pytest plugin if astropy_version < '3.0': - # With older versions of Astropy, we actually need to import the pytest - # plugins themselves in order to make them discoverable by pytest. - from astropy.tests.pytest_plugins import * + from astropy.tests.pytest_plugins import * # noqa + del pytest_report_header + ASTROPY_HEADER = True else: - # As of Astropy 3.0, the pytest plugins provided by Astropy are - # automatically made available when Astropy is installed. This means it's - # not necessary to import them here, but we still need to import global - # variables that are used for configuration. - from astropy.tests.plugins.display import (pytest_report_header, - PYTEST_HEADER_MODULES, - TESTED_VERSIONS) - -from astropy.tests.helper import enable_deprecations_as_exceptions - -## Uncomment the following line to treat all DeprecationWarnings as -## exceptions. For Astropy v2.0 or later, there are 2 additional keywords, -## as follow (although default should work for most cases). -## To ignore some packages that produce deprecation warnings on import -## (in addition to 'compiler', 'scipy', 'pygments', 'ipykernel', and -## 'setuptools'), add: -## modules_to_ignore_on_import=['module_1', 'module_2'] -## To ignore some specific deprecation warning messages for Python version -## MAJOR.MINOR or later, add: -## warnings_to_ignore_by_pyver={(MAJOR, MINOR): ['Message to ignore']} -# enable_deprecations_as_exceptions() + try: + from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS + ASTROPY_HEADER = True + except ImportError: + ASTROPY_HEADER = False + + +def pytest_configure(config): -# Customize the following lines to add/remove entries from -# the list of packages for which version numbers are displayed when running -# the tests. Making it pass for KeyError is essential in some cases when -# the package uses other astropy affiliated packages. -try: - PYTEST_HEADER_MODULES['Astropy'] = 'astropy' - del PYTEST_HEADER_MODULES['h5py'] -except KeyError: - pass - -# This is to figure out the package version, rather than -# using Astropy's -from .version import version, astropy_helpers_version - -packagename = os.path.basename(os.path.dirname(__file__)) -TESTED_VERSIONS[packagename] = version -TESTED_VERSIONS['astropy_helpers'] = astropy_helpers_version + if ASTROPY_HEADER: + + config.option.astropy_header = True + + # Customize the following lines to add/remove entries from the list of + # packages for which version numbers are displayed when running the tests. + PYTEST_HEADER_MODULES.pop('Pandas', None) + PYTEST_HEADER_MODULES['scikit-image'] = 'skimage' + + from . import __version__ + packagename = os.path.basename(os.path.dirname(__file__)) + TESTED_VERSIONS[packagename] = __version__ + +# Uncomment the last two lines in this block to treat all DeprecationWarnings as +# exceptions. For Astropy v2.0 or later, there are 2 additional keywords, +# as follow (although default should work for most cases). +# To ignore some packages that produce deprecation warnings on import +# (in addition to 'compiler', 'scipy', 'pygments', 'ipykernel', and +# 'setuptools'), add: +# modules_to_ignore_on_import=['module_1', 'module_2'] +# To ignore some specific deprecation warning messages for Python version +# MAJOR.MINOR or later, add: +# warnings_to_ignore_by_pyver={(MAJOR, MINOR): ['Message to ignore']} +# from astropy.tests.helper import enable_deprecations_as_exceptions # noqa +# enable_deprecations_as_exceptions() diff --git a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/data/README.rst b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/data/README.rst index 9cc3b1ad..382f6e76 100644 --- a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/data/README.rst +++ b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/data/README.rst @@ -4,4 +4,3 @@ Data directory This directory contains data files included with the package source code distribution. Note that this is intended only for relatively small files - large files should be externally hosted and downloaded as needed. - diff --git a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/example_mod.py b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/example_mod.py index 730ca83c..d5f3f1cd 100644 --- a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/example_mod.py +++ b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/example_mod.py @@ -41,7 +41,7 @@ def primes(imax): def do_primes(n, usecython=False): if usecython: -{% if cookiecutter.include_example_cython_code != 'y' %} +{% if cookiecutter.use_compiled_extensions != 'y' %} raise Exception("This template does not have the example C code included.") {% else %} from .example_c import primes as cprimes diff --git a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/tests/coveragerc b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/tests/coveragerc deleted file mode 100644 index 89e98ec8..00000000 --- a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/tests/coveragerc +++ /dev/null @@ -1,31 +0,0 @@ -[run] -source = {packagename} -omit = - {packagename}/_{{ cookiecutter._parent_project }}_init* - {packagename}/conftest* - {packagename}/cython_version* - {packagename}/setup_package* - {packagename}/*/setup_package* - {packagename}/*/*/setup_package* - {packagename}/tests/* - {packagename}/*/tests/* - {packagename}/*/*/tests/* - {packagename}/version* - -[report] -exclude_lines = - # Have to re-enable the standard pragma - pragma: no cover - - # Don't complain about packages we have installed - except ImportError - - # Don't complain if tests don't hit assertions - raise AssertionError - raise NotImplementedError - - # Don't complain about script hooks - def main\(.*\): - - # Ignore branches that don't pertain to this version of Python - pragma: py{ignore_python_version} \ No newline at end of file diff --git a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/tests/test_example.py b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/tests/test_example.py index cce7bd7c..446761f3 100644 --- a/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/tests/test_example.py +++ b/{{ cookiecutter.package_name }}/{{ cookiecutter.module_name }}/tests/test_example.py @@ -1,4 +1,4 @@ -{% if cookiecutter.include_example_cython_code == 'y' %} +{% if cookiecutter.use_compiled_extensions == 'y' %} def test_primes_c(): from ..example_c import primes as primes_c assert primes_c(10) == [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]