diff --git a/.cruft.json b/.cruft.json new file mode 100644 index 0000000..4dbfc10 --- /dev/null +++ b/.cruft.json @@ -0,0 +1,31 @@ +{ + "template": "https://github.com/OpenAstronomy/packaging-guide", + "commit": "ad6da17e1d8fd96456566abed30b510bf8b5f8c8", + "checkout": null, + "context": { + "cookiecutter": { + "package_name": "gunagala", + "module_name": "gunagala", + "short_description": "Performance modelling for astronomical instruments", + "author_name": "Anthony Horton", + "author_email": "anthony.horton@mq.edu.au", + "project_url": "https://github.com/AstroHuntsman/gunagala", + "license": "BSD 3-Clause", + "minimum_python_version": "3.9", + "use_compiled_extensions": "n", + "enable_dynamic_dev_versions": "n", + "include_example_code": "y", + "include_cruft_update_github_workflow": "y", + "_sphinx_theme": "alabaster", + "_parent_project": "", + "_install_requires": "", + "_copy_without_render": [ + "docs/_templates", + "docs/_static", + ".github/workflows/sub_package_update.yml" + ], + "_template": "https://github.com/OpenAstronomy/packaging-guide" + } + }, + "directory": null +} diff --git a/.github/workflows/pythontest.yml b/.github/workflows/pythontest.yml new file mode 100644 index 0000000..3c2cd42 --- /dev/null +++ b/.github/workflows/pythontest.yml @@ -0,0 +1,56 @@ +name: Python test + +on: + - pull_request + +jobs: + lint: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.12] + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Lint with flake8 + run: | + pip install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=100 --statistics + build: + needs: lint + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.9', '3.12'] + platform: [ubuntu-latest, macos-latest] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox tox-gh-actions + - name: Test with tox + run: tox + env: + PLATFORM: ${{ matrix.platform }} + - name: Upload coverage.xml + if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.12' }} + uses: actions/upload-artifact@v4 + with: + name: tox-gh-actions-coverage + path: coverage.xml + if-no-files-found: error + - name: Upload coverage.xml to codecov + if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.12' }} + uses: codecov/codecov-action@v2 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4520101..c382b83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,66 +1,171 @@ # Large data files HFI_CompMap_ThermalDustModel_2048_R1.20.fits -# Oddly this isn't in the template. -.eggs +# Mac OSX +.DS_Store -# Compiled files +# Byte-compiled / optimized / DLL files +__pycache__/ *.py[cod] -*.a -*.o +*$py.class + +# C extensions *.so -__pycache__ -# Ignore .c files by default to avoid including generated code. If you want to -# add a non-generated .c extension, use `git add -f filename.c`. -*.c +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +gunagala/version.py +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt -# Other generated files -*/version.py -*/cython_version.py -htmlcov +# Unit test / coverage reports +htmlcov/ +.tox/ +.tmp +.nox/ .coverage -MANIFEST -.ipynb_checkpoints +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache -# Sphinx +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ docs/api -docs/_build -# Eclipse editor project files -.project -.pydevproject -.settings +# PyBuilder +.pybuilder/ +target/ -# Pycharm editor project files -.idea +# Jupyter Notebook +.ipynb_checkpoints -# Floobits project files -.floo -.flooignore +# IPython +profile_default/ +ipython_config.py -# Packages/installer info -*.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg -distribute-*.tar.gz +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version -# Other -.cache -.tox -.*.sw[op] -*~ -.project -.pydevproject -.settings +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock -# Mac OSX -.DS_Store +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# visual studio code project settings +.vscode* diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 6a20fa6..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "astropy_helpers"] - path = astropy_helpers - url = https://github.com/astropy/astropy-helpers.git diff --git a/.rtd-environment.yml b/.rtd-environment.yml deleted file mode 100644 index 3755d62..0000000 --- a/.rtd-environment.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: gunagala - -channels: - - astropy - -dependencies: - - python=3.6 - - astropy - - scipy - - matplotlib - - numpy - - PyYAML - - pip: - - sphinx-astropy diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 253138b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,154 +0,0 @@ -# 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 - -os: - - linux - -# Setting sudo to false opts in to Travis-CI container-based builds. -sudo: false - -# The apt packages below are needed for sphinx builds. A full list of packages -# that can be included can be found here: -# -# https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise - -addons: - apt: - packages: - - graphviz - - texlive-latex-extra - - dvipng - -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.6 - - NUMPY_VERSION=stable - - ASTROPY_VERSION=stable - - MAIN_CMD='python setup.py' - - SETUP_CMD='test' - - PIP_DEPENDENCIES='scipy matplotlib PyYAML' - - EVENT_TYPE='pull_request push' - - - # 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='' - - # 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-ci-extras astropy' - - # If there are matplotlib or other GUI tests, uncomment the following - # line to use the X virtual framebuffer. - # - SETUP_XVFB=True - - matrix: - # Make sure that egg_info works without dependencies - #- PYTHON_VERSION=2.7 SETUP_CMD='egg_info' - - PYTHON_VERSION=3.4 SETUP_CMD='egg_info' - - PYTHON_VERSION=3.5 SETUP_CMD='egg_info' - - PYTHON_VERSION=3.6 SETUP_CMD='egg_info' - -matrix: - - # Don't wait for allowed failures - fast_finish: true - - include: - # Try MacOS X - - os: osx - env: SETUP_CMD='test' - - # Do a coverage test. - - os: linux - env: SETUP_CMD='test --coverage' - - # Check for sphinx doc build warnings - we do this first because it - # may run for a long time - - os: linux - env: SETUP_CMD='build_docs -w' - - # Now try Astropy dev with the latest Python and LTS with Python 2.7 and 3.x. - - os: linux - env: ASTROPY_VERSION=development - EVENT_TYPE='pull_request push cron' - #- os: linux - # env: PYTHON_VERSION=2.7 ASTROPY_VERSION=lts - # - os: linux - # env: ASTROPY_VERSION=lts - - # Try all 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=2.7 NUMPY_VERSION=1.9 - - os: linux - env: PYTHON_VERSION=3.4 NUMPY_VERSION=1.11 - - os: linux - env: PYTHON_VERSION=3.5 NUMPY_VERSION=1.12 - - os: linux - env: NUMPY_VERSION=1.13 - - # Try numpy pre-release - - os: linux - env: NUMPY_VERSION=prerelease - EVENT_TYPE='pull_request push cron' - - # Do a PEP8 test with pycodestyle - - os: linux - env: MAIN_CMD='pycodestyle gunagala --count' SETUP_CMD='' - - allow_failures: - # Do a PEP8 test with pycodestyle - # (allow to fail unless your code completely compliant) - #- os: linux - # env: MAIN_CMD='pycodestyle gunagala --count' SETUP_CMD='' - -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. - -script: - - $MAIN_CMD $SETUP_CMD - -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='gunagala/tests/coveragerc'; fi diff --git a/MANIFEST.in b/MANIFEST.in index d6891f4..baad47d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,6 @@ include README.rst include CHANGES.rst -include ez_setup.py -include ah_bootstrap.py include setup.cfg include gunagala/tests/coveragerc @@ -16,26 +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/ez_setup.py -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/README.rst b/README.rst index 3930192..3769aa0 100644 --- a/README.rst +++ b/README.rst @@ -16,14 +16,95 @@ the Kamilaroi/Gamilaraay language Australians have studied the night skies above Australia for at least 50000 years. To learn more about Aboriginal astronomy please visit http://www.aboriginalastronomy.com.au/. -.. image:: https://travis-ci.org/AstroHuntsman/gunagala.svg - :target: https://travis-ci.org/AstroHuntsman/gunagala - :alt: Travis Status +.. image:: https://github.com/AstroHuntsman/gunagala/actions/workflows/pythontest.yml/badge.svg + :target: https://github.com/AstroHuntsman/gunagala/actions + :alt: Python package tests -.. image:: https://coveralls.io/repos/github/AstroHuntsman/gunagala/badge.svg?branch=master - :target: https://coveralls.io/github/AstroHuntsman/gunagala?branch=master +.. image:: https://coveralls.io/repos/github/AstroHuntsman/gunagala/badge.svg?branch=develop + :target: https://coveralls.io/github/AstroHuntsman/gunagala?branch=develop :alt: Coverage status -.. image:: https://readthedocs.org/projects/gunagala/badge/?version=develop - :target: http://gunagala.readthedocs.io/en/develop/?badge=develop - :alt: Documentation Status +.. .. image:: https://readthedocs.org/projects/gunagala/badge/?version=develop +.. :target: http://gunagala.readthedocs.io/en/develop/?badge=develop +.. :alt: Documentation Status + + +Development +----------- + +To install gunagala in development mode, clone the repository and run: + +.. code-block:: bash + + git clone https://github.com/AstroHuntsman/gunagala.git + cd gunagala + pip install -e . + +To run the tests on various python versions, install tox (`pip install tox`) and run: + +.. code-block:: bash + + tox + +Or run tests in your current python environement: + +.. code-block:: bash + + cd gunagala + pip install -e .[test] + pytest + +To build the documentation, install the package in development mode and run: + +.. code-block:: bash + + pip install -e .[docs] + cd docs + make html + +Or just run: + +.. code-block:: bash + + tox -e build_docs -- -aE + +License +------- + +This project is Copyright (c) Anthony Horton and licensed under +the terms of the BSD 3-Clause license. This package is based upon +the `Openastronomy packaging guide `_ +which is licensed under the BSD 3-clause licence. See the licenses folder for +more information. + +Contributing +------------ + +We love contributions! gunagala is open source, +built on open source, and we'd love to have you hang out in our community. + +**Imposter syndrome disclaimer**: We want your help. No, really. + +There may be a little voice inside your head that is telling you that you're not +ready to be an open source contributor; that your skills aren't nearly good +enough to contribute. What could you possibly offer a project like this one? + +We assure you - the little voice in your head is wrong. If you can write code at +all, you can contribute code to open source. Contributing to open source +projects is a fantastic way to advance one's coding skills. Writing perfect code +isn't the measure of a good developer (that would disqualify all of us!); it's +trying to create something, making mistakes, and learning from those +mistakes. That's how we all improve, and we are happy to help others learn. + +Being an open source contributor doesn't just mean writing code, either. You can +help out by writing documentation, tests, or even giving feedback about the +project (and yes - that includes giving feedback about the contribution +process). Some of these contributions may be the most valuable to the project as +a whole, because you're coming to the project with fresh eyes, so you can see +the errors and assumptions that seasoned contributors have glossed over. + +Note: This disclaimer was originally written by +`Adrienne Lowe `_ for a +`PyCon talk `_, and was adapted by +gunagala based on its use in the README file for the +`MetPy project `_. diff --git a/ah_bootstrap.py b/ah_bootstrap.py deleted file mode 100644 index 786b8b1..0000000 --- a/ah_bootstrap.py +++ /dev/null @@ -1,958 +0,0 @@ -""" -This bootstrap module contains code for ensuring that the astropy_helpers -package will be importable by the time the setup.py script runs. It also -includes some workarounds to ensure that a recent-enough version of setuptools -is being used for the installation. - -This module should be the first thing imported in the setup.py of distributions -that make use of the utilities in astropy_helpers. If the distribution ships -with its own copy of astropy_helpers, this module will first attempt to import -from the shipped copy. However, it will also check PyPI to see if there are -any bug-fix releases on top of the current version that may be useful to get -past platform-specific bugs that have been fixed. When running setup.py, use -the ``--offline`` command-line option to disable the auto-upgrade checks. - -When this module is imported or otherwise executed it automatically calls a -main function that attempts to read the project's setup.cfg file, which it -checks for a configuration section called ``[ah_bootstrap]`` the presences of -that section, and options therein, determine the next step taken: If it -contains an option called ``auto_use`` with a value of ``True``, it will -automatically call the main function of this module called -`use_astropy_helpers` (see that function's docstring for full details). -Otherwise no further action is taken (however, -``ah_bootstrap.use_astropy_helpers`` may be called manually from within the -setup.py script). - -Additional options in the ``[ah_boostrap]`` section of setup.cfg have the same -names as the arguments to `use_astropy_helpers`, and can be used to configure -the bootstrap script when ``auto_use = True``. - -See https://github.com/astropy/astropy-helpers for more details, and for the -latest version of this module. -""" - -import contextlib -import errno -import imp -import io -import locale -import os -import re -import subprocess as sp -import sys - -try: - from ConfigParser import ConfigParser, RawConfigParser -except ImportError: - from configparser import ConfigParser, RawConfigParser - - -if sys.version_info[0] < 3: - _str_types = (str, unicode) - _text_type = unicode - PY3 = False -else: - _str_types = (str, bytes) - _text_type = str - PY3 = True - - -# What follows are several import statements meant to deal with install-time -# issues with either missing or misbehaving pacakges (including making sure -# setuptools itself is installed): - - -# Some pre-setuptools checks to ensure that either distribute or setuptools >= -# 0.7 is used (over pre-distribute setuptools) if it is available on the path; -# otherwise the latest setuptools will be downloaded and bootstrapped with -# ``ez_setup.py``. This used to be included in a separate file called -# setuptools_bootstrap.py; but it was combined into ah_bootstrap.py -try: - import pkg_resources - _setuptools_req = pkg_resources.Requirement.parse('setuptools>=0.7') - # This may raise a DistributionNotFound in which case no version of - # setuptools or distribute is properly installed - _setuptools = pkg_resources.get_distribution('setuptools') - if _setuptools not in _setuptools_req: - # Older version of setuptools; check if we have distribute; again if - # this results in DistributionNotFound we want to give up - _distribute = pkg_resources.get_distribution('distribute') - if _setuptools != _distribute: - # It's possible on some pathological systems to have an old version - # of setuptools and distribute on sys.path simultaneously; make - # sure distribute is the one that's used - sys.path.insert(1, _distribute.location) - _distribute.activate() - imp.reload(pkg_resources) -except: - # There are several types of exceptions that can occur here; if all else - # fails bootstrap and use the bootstrapped version - from ez_setup import use_setuptools - use_setuptools() - - -# typing as a dependency for 1.6.1+ Sphinx causes issues when imported after -# initializing submodule with ah_boostrap.py -# See discussion and references in -# https://github.com/astropy/astropy-helpers/issues/302 - -try: - import typing # noqa -except ImportError: - pass - - -# Note: The following import is required as a workaround to -# https://github.com/astropy/astropy-helpers/issues/89; if we don't import this -# module now, it will get cleaned up after `run_setup` is called, but that will -# later cause the TemporaryDirectory class defined in it to stop working when -# used later on by setuptools -try: - import setuptools.py31compat # noqa -except ImportError: - pass - - -# matplotlib can cause problems if it is imported from within a call of -# run_setup(), because in some circumstances it will try to write to the user's -# home directory, resulting in a SandboxViolation. See -# https://github.com/matplotlib/matplotlib/pull/4165 -# Making sure matplotlib, if it is available, is imported early in the setup -# process can mitigate this (note importing matplotlib.pyplot has the same -# issue) -try: - import matplotlib - matplotlib.use('Agg') - import matplotlib.pyplot -except: - # Ignore if this fails for *any* reason* - pass - - -# End compatibility imports... - - -# In case it didn't successfully import before the ez_setup checks -import pkg_resources - -from setuptools import Distribution -from setuptools.package_index import PackageIndex -from setuptools.sandbox import run_setup - -from distutils import log -from distutils.debug import DEBUG - - -# TODO: Maybe enable checking for a specific version of astropy_helpers? -DIST_NAME = 'astropy-helpers' -PACKAGE_NAME = 'astropy_helpers' - -# Defaults for other options -DOWNLOAD_IF_NEEDED = True -INDEX_URL = 'https://pypi.python.org/simple' -USE_GIT = True -OFFLINE = False -AUTO_UPGRADE = True - -# A list of all the configuration options and their required types -CFG_OPTIONS = [ - ('auto_use', bool), ('path', str), ('download_if_needed', bool), - ('index_url', str), ('use_git', bool), ('offline', bool), - ('auto_upgrade', bool) -] - - -class _Bootstrapper(object): - """ - Bootstrapper implementation. See ``use_astropy_helpers`` for parameter - documentation. - """ - - def __init__(self, path=None, index_url=None, use_git=None, offline=None, - download_if_needed=None, auto_upgrade=None): - - if path is None: - path = PACKAGE_NAME - - if not (isinstance(path, _str_types) or path is False): - raise TypeError('path must be a string or False') - - if PY3 and not isinstance(path, _text_type): - fs_encoding = sys.getfilesystemencoding() - path = path.decode(fs_encoding) # path to unicode - - self.path = path - - # Set other option attributes, using defaults where necessary - self.index_url = index_url if index_url is not None else INDEX_URL - self.offline = offline if offline is not None else OFFLINE - - # If offline=True, override download and auto-upgrade - if self.offline: - download_if_needed = False - auto_upgrade = False - - self.download = (download_if_needed - if download_if_needed is not None - else DOWNLOAD_IF_NEEDED) - self.auto_upgrade = (auto_upgrade - if auto_upgrade is not None else AUTO_UPGRADE) - - # If this is a release then the .git directory will not exist so we - # should not use git. - git_dir_exists = os.path.exists(os.path.join(os.path.dirname(__file__), '.git')) - if use_git is None and not git_dir_exists: - use_git = False - - self.use_git = use_git if use_git is not None else USE_GIT - # Declared as False by default--later we check if astropy-helpers can be - # upgraded from PyPI, but only if not using a source distribution (as in - # the case of import from a git submodule) - self.is_submodule = False - - @classmethod - def main(cls, argv=None): - if argv is None: - argv = sys.argv - - config = cls.parse_config() - config.update(cls.parse_command_line(argv)) - - auto_use = config.pop('auto_use', False) - bootstrapper = cls(**config) - - if auto_use: - # Run the bootstrapper, otherwise the setup.py is using the old - # use_astropy_helpers() interface, in which case it will run the - # bootstrapper manually after reconfiguring it. - bootstrapper.run() - - return bootstrapper - - @classmethod - def parse_config(cls): - if not os.path.exists('setup.cfg'): - return {} - - cfg = ConfigParser() - - try: - cfg.read('setup.cfg') - except Exception as e: - if DEBUG: - raise - - log.error( - "Error reading setup.cfg: {0!r}\n{1} will not be " - "automatically bootstrapped and package installation may fail." - "\n{2}".format(e, PACKAGE_NAME, _err_help_msg)) - return {} - - if not cfg.has_section('ah_bootstrap'): - return {} - - config = {} - - for option, type_ in CFG_OPTIONS: - if not cfg.has_option('ah_bootstrap', option): - continue - - if type_ is bool: - value = cfg.getboolean('ah_bootstrap', option) - else: - value = cfg.get('ah_bootstrap', option) - - config[option] = value - - return config - - @classmethod - def parse_command_line(cls, argv=None): - if argv is None: - argv = sys.argv - - config = {} - - # For now we just pop recognized ah_bootstrap options out of the - # arg list. This is imperfect; in the unlikely case that a setup.py - # custom command or even custom Distribution class defines an argument - # of the same name then we will break that. However there's a catch22 - # here that we can't just do full argument parsing right here, because - # we don't yet know *how* to parse all possible command-line arguments. - if '--no-git' in argv: - config['use_git'] = False - argv.remove('--no-git') - - if '--offline' in argv: - config['offline'] = True - argv.remove('--offline') - - return config - - def run(self): - strategies = ['local_directory', 'local_file', 'index'] - dist = None - - # First, remove any previously imported versions of astropy_helpers; - # this is necessary for nested installs where one package's installer - # is installing another package via setuptools.sandbox.run_setup, as in - # the case of setup_requires - for key in list(sys.modules): - try: - if key == PACKAGE_NAME or key.startswith(PACKAGE_NAME + '.'): - del sys.modules[key] - except AttributeError: - # Sometimes mysterious non-string things can turn up in - # sys.modules - continue - - # Check to see if the path is a submodule - self.is_submodule = self._check_submodule() - - for strategy in strategies: - method = getattr(self, 'get_{0}_dist'.format(strategy)) - dist = method() - if dist is not None: - break - else: - raise _AHBootstrapSystemExit( - "No source found for the {0!r} package; {0} must be " - "available and importable as a prerequisite to building " - "or installing this package.".format(PACKAGE_NAME)) - - # This is a bit hacky, but if astropy_helpers was loaded from a - # directory/submodule its Distribution object gets a "precedence" of - # "DEVELOP_DIST". However, in other cases it gets a precedence of - # "EGG_DIST". However, when activing the distribution it will only be - # placed early on sys.path if it is treated as an EGG_DIST, so always - # do that - dist = dist.clone(precedence=pkg_resources.EGG_DIST) - - # Otherwise we found a version of astropy-helpers, so we're done - # Just active the found distribution on sys.path--if we did a - # download this usually happens automatically but it doesn't hurt to - # do it again - # Note: Adding the dist to the global working set also activates it - # (makes it importable on sys.path) by default. - - try: - pkg_resources.working_set.add(dist, replace=True) - except TypeError: - # Some (much) older versions of setuptools do not have the - # replace=True option here. These versions are old enough that all - # bets may be off anyways, but it's easy enough to work around just - # in case... - if dist.key in pkg_resources.working_set.by_key: - del pkg_resources.working_set.by_key[dist.key] - pkg_resources.working_set.add(dist) - - @property - def config(self): - """ - A `dict` containing the options this `_Bootstrapper` was configured - with. - """ - - return dict((optname, getattr(self, optname)) - for optname, _ in CFG_OPTIONS if hasattr(self, optname)) - - def get_local_directory_dist(self): - """ - Handle importing a vendored package from a subdirectory of the source - distribution. - """ - - if not os.path.isdir(self.path): - return - - log.info('Attempting to import astropy_helpers from {0} {1!r}'.format( - 'submodule' if self.is_submodule else 'directory', - self.path)) - - dist = self._directory_import() - - if dist is None: - log.warn( - 'The requested path {0!r} for importing {1} does not ' - 'exist, or does not contain a copy of the {1} ' - 'package.'.format(self.path, PACKAGE_NAME)) - elif self.auto_upgrade and not self.is_submodule: - # A version of astropy-helpers was found on the available path, but - # check to see if a bugfix release is available on PyPI - upgrade = self._do_upgrade(dist) - if upgrade is not None: - dist = upgrade - - return dist - - def get_local_file_dist(self): - """ - Handle importing from a source archive; this also uses setup_requires - but points easy_install directly to the source archive. - """ - - if not os.path.isfile(self.path): - return - - log.info('Attempting to unpack and import astropy_helpers from ' - '{0!r}'.format(self.path)) - - try: - dist = self._do_download(find_links=[self.path]) - except Exception as e: - if DEBUG: - raise - - log.warn( - 'Failed to import {0} from the specified archive {1!r}: ' - '{2}'.format(PACKAGE_NAME, self.path, str(e))) - dist = None - - if dist is not None and self.auto_upgrade: - # A version of astropy-helpers was found on the available path, but - # check to see if a bugfix release is available on PyPI - upgrade = self._do_upgrade(dist) - if upgrade is not None: - dist = upgrade - - return dist - - def get_index_dist(self): - if not self.download: - log.warn('Downloading {0!r} disabled.'.format(DIST_NAME)) - return None - - log.warn( - "Downloading {0!r}; run setup.py with the --offline option to " - "force offline installation.".format(DIST_NAME)) - - try: - dist = self._do_download() - except Exception as e: - if DEBUG: - raise - log.warn( - 'Failed to download and/or install {0!r} from {1!r}:\n' - '{2}'.format(DIST_NAME, self.index_url, str(e))) - dist = None - - # No need to run auto-upgrade here since we've already presumably - # gotten the most up-to-date version from the package index - return dist - - def _directory_import(self): - """ - Import astropy_helpers from the given path, which will be added to - sys.path. - - Must return True if the import succeeded, and False otherwise. - """ - - # Return True on success, False on failure but download is allowed, and - # otherwise raise SystemExit - path = os.path.abspath(self.path) - - # Use an empty WorkingSet rather than the man - # pkg_resources.working_set, since on older versions of setuptools this - # will invoke a VersionConflict when trying to install an upgrade - ws = pkg_resources.WorkingSet([]) - ws.add_entry(path) - dist = ws.by_key.get(DIST_NAME) - - if dist is None: - # We didn't find an egg-info/dist-info in the given path, but if a - # setup.py exists we can generate it - setup_py = os.path.join(path, 'setup.py') - if os.path.isfile(setup_py): - with _silence(): - run_setup(os.path.join(path, 'setup.py'), - ['egg_info']) - - for dist in pkg_resources.find_distributions(path, True): - # There should be only one... - return dist - - return dist - - def _do_download(self, version='', find_links=None): - if find_links: - allow_hosts = '' - index_url = None - else: - allow_hosts = None - index_url = self.index_url - - # Annoyingly, setuptools will not handle other arguments to - # Distribution (such as options) before handling setup_requires, so it - # is not straightforward to programmatically augment the arguments which - # are passed to easy_install - class _Distribution(Distribution): - def get_option_dict(self, command_name): - opts = Distribution.get_option_dict(self, command_name) - if command_name == 'easy_install': - if find_links is not None: - opts['find_links'] = ('setup script', find_links) - if index_url is not None: - opts['index_url'] = ('setup script', index_url) - if allow_hosts is not None: - opts['allow_hosts'] = ('setup script', allow_hosts) - return opts - - if version: - req = '{0}=={1}'.format(DIST_NAME, version) - else: - req = DIST_NAME - - attrs = {'setup_requires': [req]} - - try: - if DEBUG: - _Distribution(attrs=attrs) - else: - with _silence(): - _Distribution(attrs=attrs) - - # If the setup_requires succeeded it will have added the new dist to - # the main working_set - return pkg_resources.working_set.by_key.get(DIST_NAME) - except Exception as e: - if DEBUG: - raise - - msg = 'Error retrieving {0} from {1}:\n{2}' - if find_links: - source = find_links[0] - elif index_url != INDEX_URL: - source = index_url - else: - source = 'PyPI' - - raise Exception(msg.format(DIST_NAME, source, repr(e))) - - def _do_upgrade(self, dist): - # Build up a requirement for a higher bugfix release but a lower minor - # release (so API compatibility is guaranteed) - next_version = _next_version(dist.parsed_version) - - req = pkg_resources.Requirement.parse( - '{0}>{1},<{2}'.format(DIST_NAME, dist.version, next_version)) - - package_index = PackageIndex(index_url=self.index_url) - - upgrade = package_index.obtain(req) - - if upgrade is not None: - return self._do_download(version=upgrade.version) - - def _check_submodule(self): - """ - Check if the given path is a git submodule. - - See the docstrings for ``_check_submodule_using_git`` and - ``_check_submodule_no_git`` for further details. - """ - - if (self.path is None or - (os.path.exists(self.path) and not os.path.isdir(self.path))): - return False - - if self.use_git: - return self._check_submodule_using_git() - else: - return self._check_submodule_no_git() - - def _check_submodule_using_git(self): - """ - Check if the given path is a git submodule. If so, attempt to initialize - and/or update the submodule if needed. - - This function makes calls to the ``git`` command in subprocesses. The - ``_check_submodule_no_git`` option uses pure Python to check if the given - path looks like a git submodule, but it cannot perform updates. - """ - - cmd = ['git', 'submodule', 'status', '--', self.path] - - try: - log.info('Running `{0}`; use the --no-git option to disable git ' - 'commands'.format(' '.join(cmd))) - returncode, stdout, stderr = run_cmd(cmd) - except _CommandNotFound: - # The git command simply wasn't found; this is most likely the - # case on user systems that don't have git and are simply - # trying to install the package from PyPI or a source - # distribution. Silently ignore this case and simply don't try - # to use submodules - return False - - stderr = stderr.strip() - - if returncode != 0 and stderr: - # Unfortunately the return code alone cannot be relied on, as - # earlier versions of git returned 0 even if the requested submodule - # does not exist - - # This is a warning that occurs in perl (from running git submodule) - # which only occurs with a malformatted locale setting which can - # happen sometimes on OSX. See again - # https://github.com/astropy/astropy/issues/2749 - perl_warning = ('perl: warning: Falling back to the standard locale ' - '("C").') - if not stderr.strip().endswith(perl_warning): - # Some other unknown error condition occurred - log.warn('git submodule command failed ' - 'unexpectedly:\n{0}'.format(stderr)) - return False - - # Output of `git submodule status` is as follows: - # - # 1: Status indicator: '-' for submodule is uninitialized, '+' if - # submodule is initialized but is not at the commit currently indicated - # in .gitmodules (and thus needs to be updated), or 'U' if the - # submodule is in an unstable state (i.e. has merge conflicts) - # - # 2. SHA-1 hash of the current commit of the submodule (we don't really - # need this information but it's useful for checking that the output is - # correct) - # - # 3. The output of `git describe` for the submodule's current commit - # hash (this includes for example what branches the commit is on) but - # only if the submodule is initialized. We ignore this information for - # now - _git_submodule_status_re = re.compile( - '^(?P[+-U ])(?P[0-9a-f]{40}) ' - '(?P\S+)( .*)?$') - - # The stdout should only contain one line--the status of the - # requested submodule - m = _git_submodule_status_re.match(stdout) - if m: - # Yes, the path *is* a git submodule - self._update_submodule(m.group('submodule'), m.group('status')) - return True - else: - log.warn( - 'Unexpected output from `git submodule status`:\n{0}\n' - 'Will attempt import from {1!r} regardless.'.format( - stdout, self.path)) - return False - - def _check_submodule_no_git(self): - """ - Like ``_check_submodule_using_git``, but simply parses the .gitmodules file - to determine if the supplied path is a git submodule, and does not exec any - subprocesses. - - This can only determine if a path is a submodule--it does not perform - updates, etc. This function may need to be updated if the format of the - .gitmodules file is changed between git versions. - """ - - gitmodules_path = os.path.abspath('.gitmodules') - - if not os.path.isfile(gitmodules_path): - return False - - # This is a minimal reader for gitconfig-style files. It handles a few of - # the quirks that make gitconfig files incompatible with ConfigParser-style - # files, but does not support the full gitconfig syntax (just enough - # needed to read a .gitmodules file). - gitmodules_fileobj = io.StringIO() - - # Must use io.open for cross-Python-compatible behavior wrt unicode - with io.open(gitmodules_path) as f: - for line in f: - # gitconfig files are more flexible with leading whitespace; just - # go ahead and remove it - line = line.lstrip() - - # comments can start with either # or ; - if line and line[0] in (':', ';'): - continue - - gitmodules_fileobj.write(line) - - gitmodules_fileobj.seek(0) - - cfg = RawConfigParser() - - try: - cfg.readfp(gitmodules_fileobj) - except Exception as exc: - log.warn('Malformatted .gitmodules file: {0}\n' - '{1} cannot be assumed to be a git submodule.'.format( - exc, self.path)) - return False - - for section in cfg.sections(): - if not cfg.has_option(section, 'path'): - continue - - submodule_path = cfg.get(section, 'path').rstrip(os.sep) - - if submodule_path == self.path.rstrip(os.sep): - return True - - return False - - def _update_submodule(self, submodule, status): - if status == ' ': - # The submodule is up to date; no action necessary - return - elif status == '-': - if self.offline: - raise _AHBootstrapSystemExit( - "Cannot initialize the {0} submodule in --offline mode; " - "this requires being able to clone the submodule from an " - "online repository.".format(submodule)) - cmd = ['update', '--init'] - action = 'Initializing' - elif status == '+': - cmd = ['update'] - action = 'Updating' - if self.offline: - cmd.append('--no-fetch') - elif status == 'U': - raise _AHBootstrapSystemExit( - 'Error: Submodule {0} contains unresolved merge conflicts. ' - 'Please complete or abandon any changes in the submodule so that ' - 'it is in a usable state, then try again.'.format(submodule)) - else: - log.warn('Unknown status {0!r} for git submodule {1!r}. Will ' - 'attempt to use the submodule as-is, but try to ensure ' - 'that the submodule is in a clean state and contains no ' - 'conflicts or errors.\n{2}'.format(status, submodule, - _err_help_msg)) - return - - err_msg = None - cmd = ['git', 'submodule'] + cmd + ['--', submodule] - log.warn('{0} {1} submodule with: `{2}`'.format( - action, submodule, ' '.join(cmd))) - - try: - log.info('Running `{0}`; use the --no-git option to disable git ' - 'commands'.format(' '.join(cmd))) - returncode, stdout, stderr = run_cmd(cmd) - except OSError as e: - err_msg = str(e) - else: - if returncode != 0: - err_msg = stderr - - if err_msg is not None: - log.warn('An unexpected error occurred updating the git submodule ' - '{0!r}:\n{1}\n{2}'.format(submodule, err_msg, - _err_help_msg)) - -class _CommandNotFound(OSError): - """ - An exception raised when a command run with run_cmd is not found on the - system. - """ - - -def run_cmd(cmd): - """ - Run a command in a subprocess, given as a list of command-line - arguments. - - Returns a ``(returncode, stdout, stderr)`` tuple. - """ - - try: - p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE) - # XXX: May block if either stdout or stderr fill their buffers; - # however for the commands this is currently used for that is - # unlikely (they should have very brief output) - stdout, stderr = p.communicate() - except OSError as e: - if DEBUG: - raise - - if e.errno == errno.ENOENT: - msg = 'Command not found: `{0}`'.format(' '.join(cmd)) - raise _CommandNotFound(msg, cmd) - else: - raise _AHBootstrapSystemExit( - 'An unexpected error occurred when running the ' - '`{0}` command:\n{1}'.format(' '.join(cmd), str(e))) - - - # Can fail of the default locale is not configured properly. See - # https://github.com/astropy/astropy/issues/2749. For the purposes under - # consideration 'latin1' is an acceptable fallback. - try: - stdio_encoding = locale.getdefaultlocale()[1] or 'latin1' - except ValueError: - # Due to an OSX oddity locale.getdefaultlocale() can also crash - # depending on the user's locale/language settings. See: - # http://bugs.python.org/issue18378 - stdio_encoding = 'latin1' - - # Unlikely to fail at this point but even then let's be flexible - if not isinstance(stdout, _text_type): - stdout = stdout.decode(stdio_encoding, 'replace') - if not isinstance(stderr, _text_type): - stderr = stderr.decode(stdio_encoding, 'replace') - - return (p.returncode, stdout, stderr) - - -def _next_version(version): - """ - Given a parsed version from pkg_resources.parse_version, returns a new - version string with the next minor version. - - Examples - ======== - >>> _next_version(pkg_resources.parse_version('1.2.3')) - '1.3.0' - """ - - if hasattr(version, 'base_version'): - # New version parsing from setuptools >= 8.0 - if version.base_version: - parts = version.base_version.split('.') - else: - parts = [] - else: - parts = [] - for part in version: - if part.startswith('*'): - break - parts.append(part) - - parts = [int(p) for p in parts] - - if len(parts) < 3: - parts += [0] * (3 - len(parts)) - - major, minor, micro = parts[:3] - - return '{0}.{1}.{2}'.format(major, minor + 1, 0) - - -class _DummyFile(object): - """A noop writeable object.""" - - errors = '' # Required for Python 3.x - encoding = 'utf-8' - - def write(self, s): - pass - - def flush(self): - pass - - -@contextlib.contextmanager -def _silence(): - """A context manager that silences sys.stdout and sys.stderr.""" - - old_stdout = sys.stdout - old_stderr = sys.stderr - sys.stdout = _DummyFile() - sys.stderr = _DummyFile() - exception_occurred = False - try: - yield - except: - exception_occurred = True - # Go ahead and clean up so that exception handling can work normally - sys.stdout = old_stdout - sys.stderr = old_stderr - raise - - if not exception_occurred: - sys.stdout = old_stdout - sys.stderr = old_stderr - - -_err_help_msg = """ -If the problem persists consider installing astropy_helpers manually using pip -(`pip install astropy_helpers`) or by manually downloading the source archive, -extracting it, and installing by running `python setup.py install` from the -root of the extracted source code. -""" - - -class _AHBootstrapSystemExit(SystemExit): - def __init__(self, *args): - if not args: - msg = 'An unknown problem occurred bootstrapping astropy_helpers.' - else: - msg = args[0] - - msg += '\n' + _err_help_msg - - super(_AHBootstrapSystemExit, self).__init__(msg, *args[1:]) - - -BOOTSTRAPPER = _Bootstrapper.main() - - -def use_astropy_helpers(**kwargs): - """ - Ensure that the `astropy_helpers` module is available and is importable. - This supports automatic submodule initialization if astropy_helpers is - included in a project as a git submodule, or will download it from PyPI if - necessary. - - Parameters - ---------- - - path : str or None, optional - A filesystem path relative to the root of the project's source code - that should be added to `sys.path` so that `astropy_helpers` can be - imported from that path. - - If the path is a git submodule it will automatically be initialized - and/or updated. - - The path may also be to a ``.tar.gz`` archive of the astropy_helpers - source distribution. In this case the archive is automatically - unpacked and made temporarily available on `sys.path` as a ``.egg`` - archive. - - If `None` skip straight to downloading. - - download_if_needed : bool, optional - If the provided filesystem path is not found an attempt will be made to - download astropy_helpers from PyPI. It will then be made temporarily - available on `sys.path` as a ``.egg`` archive (using the - ``setup_requires`` feature of setuptools. If the ``--offline`` option - is given at the command line the value of this argument is overridden - to `False`. - - index_url : str, optional - If provided, use a different URL for the Python package index than the - main PyPI server. - - use_git : bool, optional - If `False` no git commands will be used--this effectively disables - support for git submodules. If the ``--no-git`` option is given at the - command line the value of this argument is overridden to `False`. - - auto_upgrade : bool, optional - By default, when installing a package from a non-development source - distribution ah_boostrap will try to automatically check for patch - releases to astropy-helpers on PyPI and use the patched version over - any bundled versions. Setting this to `False` will disable that - functionality. If the ``--offline`` option is given at the command line - the value of this argument is overridden to `False`. - - offline : bool, optional - If `False` disable all actions that require an internet connection, - including downloading packages from the package index and fetching - updates to any git submodule. Defaults to `True`. - """ - - global BOOTSTRAPPER - - config = BOOTSTRAPPER.config - config.update(**kwargs) - - # Create a new bootstrapper with the updated configuration and run it - BOOTSTRAPPER = _Bootstrapper(**config) - BOOTSTRAPPER.run() diff --git a/astropy_helpers b/astropy_helpers deleted file mode 160000 index 6a6d403..0000000 --- a/astropy_helpers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6a6d40322d30739581503d5c4ab7b5264d110f81 diff --git a/docs/Makefile b/docs/Makefile index fb03f26..41c270b 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,133 +1,20 @@ -# Makefile for Sphinx documentation +# Minimal makefile for Sphinx documentation # -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . BUILDDIR = _build -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest - -#This is needed with git because git doesn't create a dir if it's empty -$(shell [ -d "_static" ] || mkdir -p _static) - +# Put it first so that "make" without argument is like "make help". help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - -clean: - -rm -rf $(BUILDDIR) - -rm -rf api - -rm -rf generated - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Astropy.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/Astropy" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - make -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." +.PHONY: help Makefile -doctest: - @echo "Run 'python setup.py test' in the root directory to run doctests " \ - @echo "in the documentation." +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 62b81cb..3c201e3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,181 +1,82 @@ -# -*- coding: utf-8 -*- -# Licensed under a 3-clause BSD style license - see LICENSE.rst +# Configuration file for the Sphinx documentation builder. # -# Astropy documentation build configuration file. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this file. -# -# All configuration values have a default. Some values are defined in -# the global Astropy configuration which is loaded here before anything else. -# See astropy.sphinx.conf for which values are set there. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('..')) -# IMPORTANT: the above commented section was generated by sphinx-quickstart, but -# is *NOT* appropriate for astropy or Astropy affiliated packages. It is left -# commented out with this explanation to make it clear why this should not be -# done. If the sys.path entry above is added, when the astropy.sphinx.conf -# import occurs, it will import the *source* version of astropy instead of the -# version installed (if invoked as "make html" or directly with sphinx), or the -# version in the build directory (if "python setup.py build_sphinx" is used). -# Thus, any C-extensions that are needed to build the documentation will *not* -# be accessible, and the documentation will not build correctly. +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config import datetime -import os -import sys - -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 * -from sphinx_astropy.conf import * +# -- Project information ----------------------------------------------------- -# Get configuration information from setup.cfg -try: - from ConfigParser import ConfigParser -except ImportError: - from configparser import ConfigParser -conf = ConfigParser() +# The full version, including alpha/beta/rc tags +from gunagala import __version__ -conf.read([os.path.join(os.path.dirname(__file__), '..', 'setup.cfg')]) -setup_cfg = dict(conf.items('metadata')) +release = __version__ -# -- General configuration ---------------------------------------------------- +project = "gunagala" +author = "Anthony Horton" +copyright = f"{datetime.datetime.now().year}, {author}" # noqa: A001 -# By default, highlight as Python 3. -highlight_language = 'python3' +# -- General configuration --------------------------------------------------- -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.2' +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named "sphinx.ext.*") or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.inheritance_diagram", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", + "sphinx.ext.doctest", + "sphinx.ext.mathjax", + "sphinx_automodapi.automodapi", + "sphinx_automodapi.smart_resolver", +] -# To perform a Sphinx version check that needs to be more specific than -# major.minor, call `check_sphinx_version("x.y.z")` here. -# check_sphinx_version("1.2.1") +# Add any paths that contain templates here, relative to this directory. +# templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns.append('_templates') - -# This is added to the end of RST files - a good place to put substitutions to -# be used globally. -rst_epilog += """ -""" - -# -- Project information ------------------------------------------------------ - -# This does not *have* to match the package name, but typically does -project = setup_cfg['package_name'] -author = setup_cfg['author'] -copyright = '{0}, {1}'.format( - datetime.datetime.now().year, setup_cfg['author']) - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] -__import__(setup_cfg['package_name']) -package = sys.modules[setup_cfg['package_name']] +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +source_suffix = ".rst" -# The short X.Y version. -version = package.__version__.split('-', 1)[0] -# The full version, including alpha/beta/rc tags. -release = package.__version__ +# The master toctree document. +master_doc = "index" +# Treat everything in single ` as a Python reference. +default_role = 'py:obj' -# -- Options for HTML output -------------------------------------------------- +# -- Options for intersphinx extension --------------------------------------- -# A NOTE ON HTML THEMES -# The global astropy configuration uses a custom theme, 'bootstrap-astropy', -# which is installed along with astropy. A different theme can be used or -# the options for this theme can be modified by overriding some of the -# variables set in the global configuration. The variables set in the -# global configuration are listed below, commented out. +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {"python": ("https://docs.python.org/", None)} - -# Add any paths that contain custom themes here, relative to this directory. -# To use a different custom theme, add the directory containing the theme. -#html_theme_path = [] +# -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. To override the custom theme, set this to the -# name of a builtin theme or the name of a custom theme in html_theme_path. -#html_theme = None - -# Please update these texts to match the name of your package. -html_theme_options = { - 'logotext1': 'guna', # white, semi-bold - 'logotext2': 'gala', # orange, light - 'logotext3': ':docs' # white, light - } - - - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = '' - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = '' - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '' - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -html_title = '{0} v{1}'.format(project, release) - -# Output file base name for HTML help builder. -htmlhelp_basename = project + 'doc' - - -# -- Options for LaTeX output ------------------------------------------------- - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [('index', project + '.tex', project + u' Documentation', - author, 'manual')] - - -# -- Options for manual page output ------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [('index', project.lower(), project + u' Documentation', - [author], 1)] - - -# -- Options for the edit_on_github extension --------------------------------- - -if eval(setup_cfg.get('edit_on_github')): - extensions += ['astropy_helpers.sphinx.ext.edit_on_github'] - - versionmod = __import__(setup_cfg['package_name'] + '.version') - edit_on_github_project = setup_cfg['github_project'] - if versionmod.version.release: - edit_on_github_branch = "v" + versionmod.version.version - else: - edit_on_github_branch = "master" - - edit_on_github_source_root = "" - edit_on_github_doc_root = "docs" - -# -- Resolving issue number to links in changelog ----------------------------- -github_issues_url = 'https://github.com/{0}/issues/'.format(setup_cfg['github_project']) +# a list of builtin themes. +html_theme = "alabaster" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ["_static"] + +# By default, when rendering docstrings for classes, sphinx.ext.autodoc will +# make docs with the class-level docstring and the class-method docstrings, +# but not the __init__ docstring, which often contains the parameters to +# class constructors across the scientific Python ecosystem. The option below +# will append the __init__ docstring to the class-level docstring when rendering +# the docs. For more options, see: +# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autoclass_content +autoclass_content = "both" + +# -- Other options ---------------------------------------------------------- \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat index 93dfe92..922152e 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -1,170 +1,35 @@ @ECHO OFF +pushd %~dp0 + REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) +set SOURCEDIR=. set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% -) if "%1" == "" goto help -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 ) -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Astropy.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Astropy.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end +popd diff --git a/ez_setup.py b/ez_setup.py deleted file mode 100644 index 800c31e..0000000 --- a/ez_setup.py +++ /dev/null @@ -1,414 +0,0 @@ -#!/usr/bin/env python - -""" -Setuptools bootstrapping installer. - -Maintained at https://github.com/pypa/setuptools/tree/bootstrap. - -Run this script to install or upgrade setuptools. - -This method is DEPRECATED. Check https://github.com/pypa/setuptools/issues/581 for more details. -""" - -import os -import shutil -import sys -import tempfile -import zipfile -import optparse -import subprocess -import platform -import textwrap -import contextlib - -from distutils import log - -try: - from urllib.request import urlopen -except ImportError: - from urllib2 import urlopen - -try: - from site import USER_SITE -except ImportError: - USER_SITE = None - -# 33.1.1 is the last version that supports setuptools self upgrade/installation. -DEFAULT_VERSION = "33.1.1" -DEFAULT_URL = "https://pypi.io/packages/source/s/setuptools/" -DEFAULT_SAVE_DIR = os.curdir -DEFAULT_DEPRECATION_MESSAGE = "ez_setup.py is deprecated and when using it setuptools will be pinned to {0} since it's the last version that supports setuptools self upgrade/installation, check https://github.com/pypa/setuptools/issues/581 for more info; use pip to install setuptools" - -MEANINGFUL_INVALID_ZIP_ERR_MSG = 'Maybe {0} is corrupted, delete it and try again.' - -log.warn(DEFAULT_DEPRECATION_MESSAGE.format(DEFAULT_VERSION)) - - -def _python_cmd(*args): - """ - Execute a command. - - Return True if the command succeeded. - """ - args = (sys.executable,) + args - return subprocess.call(args) == 0 - - -def _install(archive_filename, install_args=()): - """Install Setuptools.""" - with archive_context(archive_filename): - # installing - log.warn('Installing Setuptools') - if not _python_cmd('setup.py', 'install', *install_args): - log.warn('Something went wrong during the installation.') - log.warn('See the error message above.') - # exitcode will be 2 - return 2 - - -def _build_egg(egg, archive_filename, to_dir): - """Build Setuptools egg.""" - with archive_context(archive_filename): - # building an egg - log.warn('Building a Setuptools egg in %s', to_dir) - _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) - # returning the result - log.warn(egg) - if not os.path.exists(egg): - raise IOError('Could not build the egg.') - - -class ContextualZipFile(zipfile.ZipFile): - - """Supplement ZipFile class to support context manager for Python 2.6.""" - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.close() - - def __new__(cls, *args, **kwargs): - """Construct a ZipFile or ContextualZipFile as appropriate.""" - if hasattr(zipfile.ZipFile, '__exit__'): - return zipfile.ZipFile(*args, **kwargs) - return super(ContextualZipFile, cls).__new__(cls) - - -@contextlib.contextmanager -def archive_context(filename): - """ - Unzip filename to a temporary directory, set to the cwd. - - The unzipped target is cleaned up after. - """ - tmpdir = tempfile.mkdtemp() - log.warn('Extracting in %s', tmpdir) - old_wd = os.getcwd() - try: - os.chdir(tmpdir) - try: - with ContextualZipFile(filename) as archive: - archive.extractall() - except zipfile.BadZipfile as err: - if not err.args: - err.args = ('', ) - err.args = err.args + ( - MEANINGFUL_INVALID_ZIP_ERR_MSG.format(filename), - ) - raise - - # going in the directory - subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) - os.chdir(subdir) - log.warn('Now working in %s', subdir) - yield - - finally: - os.chdir(old_wd) - shutil.rmtree(tmpdir) - - -def _do_download(version, download_base, to_dir, download_delay): - """Download Setuptools.""" - py_desig = 'py{sys.version_info[0]}.{sys.version_info[1]}'.format(sys=sys) - tp = 'setuptools-{version}-{py_desig}.egg' - egg = os.path.join(to_dir, tp.format(**locals())) - if not os.path.exists(egg): - archive = download_setuptools(version, download_base, - to_dir, download_delay) - _build_egg(egg, archive, to_dir) - sys.path.insert(0, egg) - - # Remove previously-imported pkg_resources if present (see - # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). - if 'pkg_resources' in sys.modules: - _unload_pkg_resources() - - import setuptools - setuptools.bootstrap_install_from = egg - - -def use_setuptools( - version=DEFAULT_VERSION, download_base=DEFAULT_URL, - to_dir=DEFAULT_SAVE_DIR, download_delay=15): - """ - Ensure that a setuptools version is installed. - - Return None. Raise SystemExit if the requested version - or later cannot be installed. - """ - to_dir = os.path.abspath(to_dir) - - # prior to importing, capture the module state for - # representative modules. - rep_modules = 'pkg_resources', 'setuptools' - imported = set(sys.modules).intersection(rep_modules) - - try: - import pkg_resources - pkg_resources.require("setuptools>=" + version) - # a suitable version is already installed - return - except ImportError: - # pkg_resources not available; setuptools is not installed; download - pass - except pkg_resources.DistributionNotFound: - # no version of setuptools was found; allow download - pass - except pkg_resources.VersionConflict as VC_err: - if imported: - _conflict_bail(VC_err, version) - - # otherwise, unload pkg_resources to allow the downloaded version to - # take precedence. - del pkg_resources - _unload_pkg_resources() - - return _do_download(version, download_base, to_dir, download_delay) - - -def _conflict_bail(VC_err, version): - """ - Setuptools was imported prior to invocation, so it is - unsafe to unload it. Bail out. - """ - conflict_tmpl = textwrap.dedent(""" - The required version of setuptools (>={version}) is not available, - and can't be installed while this script is running. Please - install a more recent version first, using - 'easy_install -U setuptools'. - - (Currently using {VC_err.args[0]!r}) - """) - msg = conflict_tmpl.format(**locals()) - sys.stderr.write(msg) - sys.exit(2) - - -def _unload_pkg_resources(): - sys.meta_path = [ - importer - for importer in sys.meta_path - if importer.__class__.__module__ != 'pkg_resources.extern' - ] - del_modules = [ - name for name in sys.modules - if name.startswith('pkg_resources') - ] - for mod_name in del_modules: - del sys.modules[mod_name] - - -def _clean_check(cmd, target): - """ - Run the command to download target. - - If the command fails, clean up before re-raising the error. - """ - try: - subprocess.check_call(cmd) - except subprocess.CalledProcessError: - if os.access(target, os.F_OK): - os.unlink(target) - raise - - -def download_file_powershell(url, target): - """ - Download the file at url to target using Powershell. - - Powershell will validate trust. - Raise an exception if the command cannot complete. - """ - target = os.path.abspath(target) - ps_cmd = ( - "[System.Net.WebRequest]::DefaultWebProxy.Credentials = " - "[System.Net.CredentialCache]::DefaultCredentials; " - '(new-object System.Net.WebClient).DownloadFile("%(url)s", "%(target)s")' - % locals() - ) - cmd = [ - 'powershell', - '-Command', - ps_cmd, - ] - _clean_check(cmd, target) - - -def has_powershell(): - """Determine if Powershell is available.""" - if platform.system() != 'Windows': - return False - cmd = ['powershell', '-Command', 'echo test'] - with open(os.path.devnull, 'wb') as devnull: - try: - subprocess.check_call(cmd, stdout=devnull, stderr=devnull) - except Exception: - return False - return True -download_file_powershell.viable = has_powershell - - -def download_file_curl(url, target): - cmd = ['curl', url, '--location', '--silent', '--output', target] - _clean_check(cmd, target) - - -def has_curl(): - cmd = ['curl', '--version'] - with open(os.path.devnull, 'wb') as devnull: - try: - subprocess.check_call(cmd, stdout=devnull, stderr=devnull) - except Exception: - return False - return True -download_file_curl.viable = has_curl - - -def download_file_wget(url, target): - cmd = ['wget', url, '--quiet', '--output-document', target] - _clean_check(cmd, target) - - -def has_wget(): - cmd = ['wget', '--version'] - with open(os.path.devnull, 'wb') as devnull: - try: - subprocess.check_call(cmd, stdout=devnull, stderr=devnull) - except Exception: - return False - return True -download_file_wget.viable = has_wget - - -def download_file_insecure(url, target): - """Use Python to download the file, without connection authentication.""" - src = urlopen(url) - try: - # Read all the data in one block. - data = src.read() - finally: - src.close() - - # Write all the data in one block to avoid creating a partial file. - with open(target, "wb") as dst: - dst.write(data) -download_file_insecure.viable = lambda: True - - -def get_best_downloader(): - downloaders = ( - download_file_powershell, - download_file_curl, - download_file_wget, - download_file_insecure, - ) - viable_downloaders = (dl for dl in downloaders if dl.viable()) - return next(viable_downloaders, None) - - -def download_setuptools( - version=DEFAULT_VERSION, download_base=DEFAULT_URL, - to_dir=DEFAULT_SAVE_DIR, delay=15, - downloader_factory=get_best_downloader): - """ - Download setuptools from a specified location and return its filename. - - `version` should be a valid setuptools version number that is available - as an sdist for download under the `download_base` URL (which should end - with a '/'). `to_dir` is the directory where the egg will be downloaded. - `delay` is the number of seconds to pause before an actual download - attempt. - - ``downloader_factory`` should be a function taking no arguments and - returning a function for downloading a URL to a target. - """ - # making sure we use the absolute path - to_dir = os.path.abspath(to_dir) - zip_name = "setuptools-%s.zip" % version - url = download_base + zip_name - saveto = os.path.join(to_dir, zip_name) - if not os.path.exists(saveto): # Avoid repeated downloads - log.warn("Downloading %s", url) - downloader = downloader_factory() - downloader(url, saveto) - return os.path.realpath(saveto) - - -def _build_install_args(options): - """ - Build the arguments to 'python setup.py install' on the setuptools package. - - Returns list of command line arguments. - """ - return ['--user'] if options.user_install else [] - - -def _parse_args(): - """Parse the command line for options.""" - parser = optparse.OptionParser() - parser.add_option( - '--user', dest='user_install', action='store_true', default=False, - help='install in user site package') - parser.add_option( - '--download-base', dest='download_base', metavar="URL", - default=DEFAULT_URL, - help='alternative URL from where to download the setuptools package') - parser.add_option( - '--insecure', dest='downloader_factory', action='store_const', - const=lambda: download_file_insecure, default=get_best_downloader, - help='Use internal, non-validating downloader' - ) - parser.add_option( - '--version', help="Specify which version to download", - default=DEFAULT_VERSION, - ) - parser.add_option( - '--to-dir', - help="Directory to save (and re-use) package", - default=DEFAULT_SAVE_DIR, - ) - options, args = parser.parse_args() - # positional arguments are ignored - return options - - -def _download_args(options): - """Return args for download_setuptools function from cmdline args.""" - return dict( - version=options.version, - download_base=options.download_base, - downloader_factory=options.downloader_factory, - to_dir=options.to_dir, - ) - - -def main(): - """Install or upgrade setuptools and EasyInstall.""" - options = _parse_args() - archive = download_setuptools(**_download_args(options)) - return _install(archive, _build_install_args(options)) - -if __name__ == '__main__': - sys.exit(main()) diff --git a/gunagala/__init__.py b/gunagala/__init__.py index 196dc84..3404065 100644 --- a/gunagala/__init__.py +++ b/gunagala/__init__.py @@ -7,14 +7,7 @@ SNR/ETC/sensitivity limit calculations and generation of simulated data. """ -# Packages may add whatever they like to this file, but -# should keep this content at the top. -# ---------------------------------------------------------------------------- -from ._astropy_init import * -# ---------------------------------------------------------------------------- +from .version import version as __version__ -if not _ASTROPY_SETUP_: - # For egg_info test builds to pass, put package imports here. - - #from .example_mod import * - pass +# Then you can be explicit to control what ends up in the namespace, +# __all__ = ['do_primes'] #original example from https://packaging-guide.openastronomy.org/ diff --git a/gunagala/_astropy_init.py b/gunagala/_astropy_init.py deleted file mode 100644 index 73a52fb..0000000 --- a/gunagala/_astropy_init.py +++ /dev/null @@ -1,143 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -__all__ = ['__version__', '__githash__', 'test'] - -# this indicates whether or not we are in the package's setup.py -try: - _ASTROPY_SETUP_ -except NameError: - from sys import version_info - if version_info[0] >= 3: - import builtins - else: - import __builtin__ as builtins - builtins._ASTROPY_SETUP_ = False - -try: - from .version import version as __version__ -except ImportError: - __version__ = '' -try: - from .version import githash as __githash__ -except ImportError: - __githash__ = '' - - -# set up the test command -def _get_test_runner(): - import os - from astropy.tests.helper import TestRunner - return TestRunner(os.path.dirname(__file__)) - - -def test(package=None, test_path=None, args=None, plugins=None, - verbose=False, pastebin=None, remote_data=False, pep8=False, - pdb=False, coverage=False, open_files=False, **kwargs): - """ - Run the tests using `py.test `__. A proper set - of arguments is constructed and passed to `pytest.main`_. - - .. _py.test: http://pytest.org/latest/ - .. _pytest.main: http://pytest.org/latest/builtin.html#pytest.main - - Parameters - ---------- - package : str, optional - The name of a specific package to test, e.g. 'io.fits' or 'utils'. - If nothing is specified all default tests are run. - - test_path : str, optional - Specify location to test by path. May be a single file or - directory. Must be specified absolutely or relative to the - calling directory. - - args : str, optional - Additional arguments to be passed to pytest.main_ in the ``args`` - keyword argument. - - plugins : list, optional - Plugins to be passed to pytest.main_ in the ``plugins`` keyword - argument. - - verbose : bool, optional - Convenience option to turn on verbose output from py.test_. Passing - True is the same as specifying ``'-v'`` in ``args``. - - pastebin : {'failed','all',None}, optional - Convenience option for turning on py.test_ pastebin output. Set to - ``'failed'`` to upload info for failed tests, or ``'all'`` to upload - info for all tests. - - remote_data : bool, optional - Controls whether to run tests marked with @remote_data. These - tests use online data and are not run by default. Set to True to - run these tests. - - pep8 : bool, optional - Turn on PEP8 checking via the `pytest-pep8 plugin - `_ and disable normal - tests. Same as specifying ``'--pep8 -k pep8'`` in ``args``. - - pdb : bool, optional - Turn on PDB post-mortem analysis for failing tests. Same as - specifying ``'--pdb'`` in ``args``. - - coverage : bool, optional - Generate a test coverage report. The result will be placed in - the directory htmlcov. - - open_files : bool, optional - Fail when any tests leave files open. Off by default, because - this adds extra run time to the test suite. Requires the - `psutil `_ package. - - parallel : int, optional - When provided, run the tests in parallel on the specified - number of CPUs. If parallel is negative, it will use the all - the cores on the machine. Requires the - `pytest-xdist `_ plugin - installed. Only available when using Astropy 0.3 or later. - - kwargs - Any additional keywords passed into this function will be passed - on to the astropy test runner. This allows use of test-related - functionality implemented in later versions of astropy without - explicitly updating the package template. - - """ - test_runner = _get_test_runner() - return test_runner.run_tests( - package=package, test_path=test_path, args=args, - plugins=plugins, verbose=verbose, pastebin=pastebin, - remote_data=remote_data, pep8=pep8, pdb=pdb, - coverage=coverage, open_files=open_files, **kwargs) - -if not _ASTROPY_SETUP_: # noqa - import os - from warnings import warn - from astropy.config.configuration import ( - update_default_config, - ConfigurationDefaultMissingError, - ConfigurationDefaultMissingWarning) - - # add these here so we only need to cleanup the namespace at the end - config_dir = None - - if not os.environ.get('ASTROPY_SKIP_CONFIG_UPDATE', False): - config_dir = os.path.dirname(__file__) - config_template = os.path.join(config_dir, __package__ + ".cfg") - if os.path.isfile(config_template): - try: - update_default_config( - __package__, config_dir, version=__version__) - except TypeError as orig_error: - try: - update_default_config(__package__, config_dir) - except ConfigurationDefaultMissingError as e: - wmsg = (e.args[0] + - " Cannot install default profile. If you are " - "importing from source, this is expected.") - warn(ConfigurationDefaultMissingWarning(wmsg)) - del e - except Exception: - raise orig_error diff --git a/gunagala/config.py b/gunagala/config.py index 87b7631..08c7e08 100644 --- a/gunagala/config.py +++ b/gunagala/config.py @@ -135,7 +135,7 @@ def save_config(path, config, clobber=True): def _add_to_conf(config, fn): try: with open(fn, 'r') as f: - c = yaml.load(f.read()) + c = yaml.load(f.read(), Loader=yaml.SafeLoader) if c is not None and isinstance(c, dict): config.update(c) except IOError: # pragma: no cover diff --git a/gunagala/conftest.py b/gunagala/conftest.py deleted file mode 100644 index ebab8a1..0000000 --- a/gunagala/conftest.py +++ /dev/null @@ -1,57 +0,0 @@ -# This file is used to configure the behavior of pytest when using the Astropy -# test infrastructure. - -from astropy.version import version as astropy_version -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 * -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_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() - -## Uncomment and 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' -# PYTEST_HEADER_MODULES['scikit-image'] = 'skimage' -# del PYTEST_HEADER_MODULES['h5py'] -# except (NameError, KeyError): # NameError is needed to support Astropy < 1.0 -# pass - -## Uncomment the following lines to display the version number of the -## package rather than the version number of Astropy in the top line when -## running the tests. -# import os -# -## This is to figure out the package version, rather than -## using Astropy's -# try: -# from .version import version -# except ImportError: -# version = 'dev' -# -# try: -# packagename = os.path.basename(os.path.dirname(__file__)) -# TESTED_VERSIONS[packagename] = version -# except NameError: # Needed to support Astropy <= 1.0.0 -# pass diff --git a/gunagala/imager.py b/gunagala/imager.py index 50b9fcd..8884783 100644 --- a/gunagala/imager.py +++ b/gunagala/imager.py @@ -363,7 +363,7 @@ def extended_source_signal_noise(self, surface_brightness, filter_name, total_ex raise ValueError( "Cannot specify pixel binning with calculation type 'per arcsecond squared'!") - if surface_brightness: + if surface_brightness is not None: # Given a source brightness if callable(surface_brightness): # Surface brightness is a callable, should return surface brightness as a function of wavelength @@ -406,7 +406,7 @@ def extended_source_signal_noise(self, surface_brightness, filter_name, total_ex signal = (rate * total_exp_time).to(u.electron / u.pixel) # If calculating the signal & noise for the sky itself need to avoid double counting it here sky_counts = self.sky_rate[filter_name] * \ - total_exp_time if surface_brightness else 0 * u.electron / u.pixel + total_exp_time if surface_brightness is not None else 0 * u.electron / u.pixel dark_counts = self.camera.dark_current * total_exp_time total_read_noise = number_subs**0.5 * self.camera.read_noise @@ -416,7 +416,7 @@ def extended_source_signal_noise(self, surface_brightness, filter_name, total_ex # Saturation check if saturation_check: - if surface_brightness: + if surface_brightness is not None: saturated = self._is_saturated(rate, sub_exp_time, filter_name) else: # Sky counts already included in _is_saturated, need to avoid counting them twice @@ -549,7 +549,7 @@ def extended_source_etc(self, surface_brightness, filter_name, snr_target, sub_e # pixel area to convert it to a per pixel value. snr_target = snr_target * self.pixel_scale / (u.arcsecond / u.pixel) - if surface_brightness: + if surface_brightness is not None: # Given a source brightness if not isinstance(surface_brightness, u.Quantity): surface_brightness = surface_brightness * u.ABmag @@ -568,7 +568,7 @@ def extended_source_etc(self, surface_brightness, filter_name, snr_target, sub_e # If required total exposure time is much greater than the length of a sub-exposure then # all noise sources (including read noise) are proportional to t^0.5 and we can use a # simplified expression to estimate total exposure time. - if surface_brightness: + if surface_brightness is not None: noise_squared_rate = ((rate + self.sky_rate[filter_name] + self.camera.dark_current) * (u.electron / u.pixel) + @@ -589,7 +589,7 @@ def extended_source_etc(self, surface_brightness, filter_name, snr_target, sub_e number_subs = np.ceil(total_exp_time / sub_exp_time) if saturation_check: - if surface_brightness: + if surface_brightness is not None: saturated = self._is_saturated(rate, sub_exp_time, filter_name) else: # Sky counts already included in _is_saturated, need to avoid counting them twice @@ -1236,7 +1236,7 @@ def extended_source_saturation_exp(self, surface_brightness, filter_name, n_sigm raise ValueError("This Imager has no filter '{}'!".format(filter_name)) if not isinstance(surface_brightness, u.Quantity): - brightness = brightness * u.ABmag + surface_brightness = surface_brightness * u.ABmag try: # If surface brightness is a count rate this should work @@ -1346,19 +1346,19 @@ def exp_time_sequence(self, if filter_name not in self.filter_names: raise ValueError("This Imager has no filter '{}'!".format(filter_name)) - if bool(bright_limit) == bool(shortest_exp_time): + if (bright_limit is not None and shortest_exp_time is not None) or (bright_limit is None and shortest_exp_time is None): raise ValueError( "One and only one of bright_limit and shortest_exp_time must be specified!") - if bool(faint_limit) == bool(num_long_exp): - raise ValueError("one and only one of faint_limit and num_long_exp must be specified!") + if (faint_limit is not None and num_long_exp is not None) or (faint_limit is None and num_long_exp is None): + raise ValueError("One and only one of faint_limit and num_long_exp must be specified!") longest_exp_time = ensure_unit(longest_exp_time, u.second) if longest_exp_time < self.camera.minimum_exposure: raise ValueError( "Longest exposure time shorter than minimum exposure time of the camera!") - if bright_limit: + if bright_limit is not None: # First calculate exposure time that will just saturate on the brightest sources. shortest_exp_time = self.point_source_saturation_exp(bright_limit, filter_name) else: @@ -1367,7 +1367,7 @@ def exp_time_sequence(self, # If the brightest sources won't saturate even for the longest requested exposure time then HDR mode isn't # necessary and we can just use the normal ETC to create a boring exposure time list. if shortest_exp_time >= longest_exp_time: - if faint_limit: + if faint_limit is not None: total_exp_time = self.point_source_etc(brightness=faint_limit, filter_name=filter_name, sub_exp_time=longest_exp_time, @@ -1395,7 +1395,7 @@ def exp_time_sequence(self, exp_times = [shortest_exp_time.to(u.second) * exp_time_ratio ** i for i in range(num_exp_times)] - if faint_limit: + if faint_limit is not None: num_long_exp = 0 # Signals and noises from each of the sub exposures in the HDR sequence signals, noises = self.point_source_signal_noise(brightness=faint_limit, diff --git a/gunagala/matplolibrc b/gunagala/matplolibrc deleted file mode 100644 index 956d43b..0000000 --- a/gunagala/matplolibrc +++ /dev/null @@ -1,2 +0,0 @@ -# Functions in utils plotting to file should use the Agg backend to ensure reliable cross platform operation -backend : Agg diff --git a/gunagala/optical_filter.py b/gunagala/optical_filter.py index fc8eba7..8ff4a8d 100644 --- a/gunagala/optical_filter.py +++ b/gunagala/optical_filter.py @@ -73,7 +73,7 @@ def __init__(self, self.apply_aoi = apply_aoi self.n_eff = n_eff - if theta_range: + if theta_range is not None: self._theta_range = ensure_unit(theta_range, u.radian) else: self._theta_range = None @@ -221,8 +221,8 @@ def transmission(self, waves, theta_range=None): """ waves = ensure_unit(waves, u.nm) - if self.apply_aoi and (theta_range or self.theta_range): - if theta_range: + if self.apply_aoi and (theta_range is not None or self.theta_range is not None): + if theta_range is not None: theta_range = ensure_unit(theta_range, u.radian) else: theta_range = self.theta_range @@ -260,14 +260,14 @@ def _update_properties(self): self._peak = self._transmission.max() self._lambda_peak = self.wavelengths[self._transmission.argmax()] above_half_max = np.arange(len(self._transmission))[self._transmission > 0.5 * self._peak] - if self.apply_aoi and self.theta_range: + if self.apply_aoi and self.theta_range is not None: blue_half_max_a = self.wavelengths[above_half_max[0] - 1] * \ (1 - (np.sin(self.theta_range.max() / self.n_eff))**2)**0.5 else: blue_half_max_a = self.wavelengths[above_half_max[0] - 1] blue_half_max_b = self.wavelengths[above_half_max[0]] - if self.apply_aoi and self.theta_range: + if self.apply_aoi and self.theta_range is not None: red_half_max_a = self.wavelengths[above_half_max[-1]] * \ (1 - (np.sin(self.theta_range.max() / self.n_eff))**2)**0.5 else: @@ -279,7 +279,7 @@ def _update_properties(self): wave1 = self._params['wave1'] wave2 = self._params['wave2'] self._lambda_peak = (wave1 + wave2) / 2 - if self.apply_aoi and self.theta_range: + if self.apply_aoi and self.theta_range is not None: blue_half_max_a = (wave1 - (wave2 - wave1)) * \ (1 - (np.sin(self.theta_range.max() / self.n_eff))**2)**0.5 else: diff --git a/gunagala/psf.py b/gunagala/psf.py index acbe974..5a84db3 100644 --- a/gunagala/psf.py +++ b/gunagala/psf.py @@ -358,7 +358,7 @@ def pixellated(self, size=(21, 21), offsets=(0.0, 0.0)): pixellated PSF will be somewhat less due to truncation of the PSF wings by the edge of the image. """ - size = np.array(size, dtype=np.int) + size = np.array(size, dtype=int) offsets = np.array(offsets) # Only want to caclulate resampled PSF for positions that fall within the PSF data, # otherwise end up filling the RAM with lots of double precision zeros. @@ -378,7 +378,7 @@ def pixellated(self, size=(21, 21), offsets=(0.0, 0.0)): # Move origin to output array origin limits = limits + (size - 1) / 2 # Round limits to the centres of the pixels containing the boundary - limits = np.rint(limits).astype(np.int) + limits = np.rint(limits).astype(int) # Crop to output array edges limits = np.array((np.where(limits[0] >= 0, limits[0], 0), np.where(limits[1] < size, limits[1], size - 1))) diff --git a/gunagala/sky.py b/gunagala/sky.py index c4d7b77..0685757 100644 --- a/gunagala/sky.py +++ b/gunagala/sky.py @@ -112,9 +112,9 @@ class ZodiacalLight(Sky): # Data from table 17, Leinert et al (1997). _llsun = np.array([0, 5 ,10, 15, 20, 25, 30, 35, 40, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180]) _beta = np.array([0, 5, 10, 15, 20, 25, 30, 45, 60, 75]) - _zl_scale = np.array([[np.NaN, np.NaN, np.NaN, 3140, 1610, 985, 640, 275, 150, 100], \ - [np.NaN, np.NaN, np.NaN, 2940, 1540, 945, 625, 271, 150, 100], \ - [np.NaN, np.NaN, 4740, 2470, 1370, 865, 590, 264, 148, 100], \ + _zl_scale = np.array([[np.nan, np.nan, np.nan, 3140, 1610, 985, 640, 275, 150, 100], \ + [np.nan, np.nan, np.nan, 2940, 1540, 945, 625, 271, 150, 100], \ + [np.nan, np.nan, 4740, 2470, 1370, 865, 590, 264, 148, 100], \ [11500, 6780, 3440, 1860, 1110, 755, 525, 251, 146, 100], \ [6400, 4480, 2410, 1410, 910, 635, 454, 237, 141, 99], \ [3840, 2830, 1730, 1100, 749, 545, 410, 223, 136, 97], \ diff --git a/gunagala/tests/coveragerc b/gunagala/tests/coveragerc deleted file mode 100644 index 6ad3621..0000000 --- a/gunagala/tests/coveragerc +++ /dev/null @@ -1,32 +0,0 @@ -[run] -source = {packagename} -omit = - {packagename}/_astropy_init* - {packagename}/conftest* - {packagename}/cython_version* - {packagename}/setup_package* - {packagename}/*/setup_package* - {packagename}/*/*/setup_package* - {packagename}/tests/* - {packagename}/*/tests/* - {packagename}/*/*/tests/* - {packagename}/version* - {packagename}/astroimsim* - -[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} diff --git a/gunagala/tests/setup_package.py b/gunagala/tests/setup_package.py deleted file mode 100644 index 118325c..0000000 --- a/gunagala/tests/setup_package.py +++ /dev/null @@ -1,11 +0,0 @@ -# import os - -# If this package has tests data in the tests/data directory, add them to -# the paths here, see commented example -paths = ['coveragerc', -# os.path.join('data', '*fits') - ] - -def get_package_data(): - return { - _ASTROPY_PACKAGE_NAME_ + '.tests': paths} diff --git a/gunagala/tests/test_camera.py b/gunagala/tests/test_camera.py index 90a00ed..e8401b9 100644 --- a/gunagala/tests/test_camera.py +++ b/gunagala/tests/test_camera.py @@ -2,6 +2,8 @@ from scipy import stats import astropy.units as u +import numpy as np + from gunagala.camera import Camera @@ -53,8 +55,9 @@ def test_dark_frame(): dark_current_dist=dist, dark_current_seed=42) assert isinstance(ccd.dark_frame, u.Quantity) - assert (ccd.dark_frame.shape * u.pixel == ccd.resolution).all() - fitted_params = stats.lognorm.fit(ccd.dark_frame.value[0:100,0:100]) + assert ccd.dark_frame.shape[0] * u.pixel == ccd.resolution[0] + assert ccd.dark_frame.shape[1] * u.pixel == ccd.resolution[1] + fitted_params = stats.lognorm.fit(np.array(ccd.dark_frame.value[0:100,0:100].flatten())) assert fitted_params[0] == pytest.approx(shape, rel=0.02) assert fitted_params[1] == pytest.approx(loc, rel=0.02) assert fitted_params[2] == pytest.approx(scale, rel=0.02) diff --git a/gunagala/tests/test_imager.py b/gunagala/tests/test_imager.py index f5ba6d9..ef34143 100644 --- a/gunagala/tests/test_imager.py +++ b/gunagala/tests/test_imager.py @@ -130,7 +130,7 @@ def test_time_calculations(imager, filter_name): # Round trip test, including rounding down. total_elapsed_time = imager.total_elapsed_time(exp_list) - assert imager.total_exposure_time(total_elapsed_time + sub_exp_time / 2, sub_exp_time) + assert imager.total_exposure_time(total_elapsed_time + sub_exp_time / 2, sub_exp_time) == total_exposure_time def test_extended_snr(imager, filter_name): sb = 25 * u.ABmag diff --git a/licenses/README.rst b/licenses/README.rst index d09b0ec..67b82f6 100644 --- a/licenses/README.rst +++ b/licenses/README.rst @@ -3,3 +3,7 @@ Licenses This directory holds license and credit information for the package, works the package is derived from, and/or datasets. + +Ensure that you pick a package licence which is in this folder and it matches +the one mentioned in the top level README.rst file. If you are using the +pre-rendered version of this template check for the word 'Other' in the README. diff --git a/licenses/TEMPLATE_LICENSE.rst b/licenses/TEMPLATE_LICENSE.rst new file mode 100644 index 0000000..f29177b --- /dev/null +++ b/licenses/TEMPLATE_LICENSE.rst @@ -0,0 +1,31 @@ +This project is based upon the Astropy package template +(https://github.com/astropy/package-template/) which is licenced under the terms +of the following licence. + +--- + +Copyright (c) 2018, Astropy Developers +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. +* Neither the name of the Astropy Team nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4061200 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,100 @@ +[build-system] +requires = [ + "setuptools>=62.1", + "setuptools_scm[toml]>=6.2", + "wheel",] +build-backend = "setuptools.build_meta" + +[project] +name = "gunagala" +description = "Performance modelling for astronomical instruments" +requires-python = ">=3.9" +readme = { file = "README.rst", content-type = "text/x-rst" } +license = { file = "licenses/LICENSE.rst" } +authors = [ + { name = "Anthony Horton", email = "anthony.horton@mq.edu.au" }, +] +dependencies = [ + "numpy", + "astropy>=6.0", + "scipy", + "matplotlib", + "numpy", + "PyYAML" + ] +dynamic = ["version"] + +#[project.scripts] +# open_astronomy_package_template_example = "gunagala.example_mod:main" + +[project.optional-dependencies] +test = [ + "pytest", + "pytest-doctestplus", + "pytest-cov", + "tox" +] +docs = [ + "sphinx", + "sphinx-automodapi", +] +[project.urls] +repository = "https://github.com/AstroHuntsman/gunagala" + +[tool.setuptools] +zip-safe = false +include-package-data = true + +[tool.setuptools.packages.find] + + +[tool.setuptools_scm] +write_to = "gunagala/version.py" + +[tool.pytest.ini_options] +testpaths = [ + "gunagala", + "docs", +] +doctest_plus = "enabled" +text_file_format = "rst" +addopts = "--doctest-rst" + +[tool.coverage.run] +omit = [ + "gunagala/__init*", + "gunagala/conftest.py", + "gunagala/*setup_package*", + "gunagala/tests/*", + "gunagala/*/tests/*", + "gunagala/extern/*", + "gunagala/version*", + "*/gunagala/__init*", + "*/gunagala/conftest.py", + "*/gunagala/*setup_package*", + "*/gunagala/tests/*", + "*/gunagala/*/tests/*", + "*/gunagala/extern/*", + "*/gunagala/version*", +] + +[tool.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_", + # typing.TYPE_CHECKING is False at runtime + "if TYPE_CHECKING:", + # Ignore typing overloads + "@overload", +] diff --git a/readthedocs.yml b/readthedocs.yml deleted file mode 100644 index a518486..0000000 --- a/readthedocs.yml +++ /dev/null @@ -1,9 +0,0 @@ -conda: - file: .rtd-environment.yml - -build: - image: latest - -python: - version: 3.6 - setup_py_install: true diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index ce2c837..0000000 --- a/setup.cfg +++ /dev/null @@ -1,57 +0,0 @@ -[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 -doctest_plus = enabled -addopts = -p no:warnings - -[ah_bootstrap] -auto_use = True - -[pycodestyle] -# E101 - mix of tabs and spaces -# W191 - use of tabs -# W291 - trailing whitespace -# W292 - no newline at end of file -# W293 - trailing whitespace -# W391 - blank line at end of file -# E111 - 4 spaces per indentation level -# E112 - 4 spaces per indentation level -# E113 - 4 spaces per indentation level -# E901 - SyntaxError or IndentationError -# E902 - IOError -select = E101,W191,W291,W292,W293,W391,E111,E112,E113,E901,E902 -exclude = extern,sphinx,*parsetab.py - -[metadata] -package_name = gunagala -description = Performance modelling for astronomical instruments -long_description = This is a Python package for modelling the performance of astronomical instruments, including SNR/ETC/sensitivity limit calculations and generation of simulated data. -author = Anthony Horton -author_email = anthony.horton@aao.gov.au -license = BSD 3-Clause -url = https://github.com/AstroHuntsman/gunagala.git -edit_on_github = False -github_project = AstroHuntsman/gunagala -# install_requires should be formatted as a comma-separated list, e.g.: -# install_requires = astropy, scipy, matplotlib -install_requires = astropy, pyYAML, numpy, scipy, matplotlib -# version should be PEP386 compatible (http://www.python.org/dev/peps/pep-0386) -version = 0.1.dev0 - -[entry_points] - -#astropy-package-template-example = packagename.example_mod:main diff --git a/setup.py b/setup.py index b9cce97..220ac08 100755 --- a/setup.py +++ b/setup.py @@ -1,142 +1,4 @@ #!/usr/bin/env python -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -import glob -import os -import sys - -import ah_bootstrap from setuptools import setup -# A dirty hack to get around some early import/configurations ambiguities -if sys.version_info[0] >= 3: - import builtins -else: - import __builtin__ as builtins -builtins._ASTROPY_SETUP_ = True - -from astropy_helpers.setup_helpers import (register_commands, get_debug_option, - get_package_info) -from astropy_helpers.git_helpers import get_git_devstr -from astropy_helpers.version_helpers import generate_version_py - -# Get some values from the setup.cfg -try: - from ConfigParser import ConfigParser -except ImportError: - from configparser import ConfigParser - -conf = ConfigParser() -conf.read(['setup.cfg']) -metadata = dict(conf.items('metadata')) - -PACKAGENAME = metadata.get('package_name', 'packagename') -DESCRIPTION = metadata.get('description', 'packagename') -AUTHOR = metadata.get('author', 'Astropy Developers') -AUTHOR_EMAIL = metadata.get('author_email', '') -LICENSE = metadata.get('license', 'unknown') -URL = metadata.get('url', 'http://astropy.org') - -# order of priority for long_description: -# (1) set in setup.cfg, -# (2) load LONG_DESCRIPTION.rst, -# (3) load README.rst, -# (4) package docstring -readme_glob = 'README*' -_cfg_long_description = metadata.get('long_description', '') -if _cfg_long_description: - LONG_DESCRIPTION = _cfg_long_description - -elif os.path.exists('LONG_DESCRIPTION.rst'): - with open('LONG_DESCRIPTION.rst') as f: - LONG_DESCRIPTION = f.read() - -elif len(glob.glob(readme_glob)) > 0: - with open(glob.glob(readme_glob)[0]) as f: - LONG_DESCRIPTION = f.read() - -else: - # Get the long description from the package's docstring - __import__(PACKAGENAME) - package = sys.modules[PACKAGENAME] - LONG_DESCRIPTION = package.__doc__ - -# 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_ = PACKAGENAME - -# VERSION should be PEP440 compatible (http://www.python.org/dev/peps/pep-0440) -VERSION = metadata.get('version', '0.0.dev0') - -# Indicates if this version is a release version -RELEASE = 'dev' not in VERSION - -if not RELEASE: - VERSION += get_git_devstr(False) - -# Populate the dict of setup command overrides; this should be done before -# invoking any other functionality from distutils since it can potentially -# modify distutils' behavior. -cmdclassd = register_commands(PACKAGENAME, VERSION, RELEASE) - -# Freeze build information in version.py -generate_version_py(PACKAGENAME, VERSION, RELEASE, - get_debug_option(PACKAGENAME)) - -# Treat everything in scripts except README* as a script to be installed -scripts = [fname for fname in glob.glob(os.path.join('scripts', '*')) - if not os.path.basename(fname).startswith('README')] - - -# 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() - -# Add the project-global data -package_info['package_data'].setdefault(PACKAGENAME, []) -package_info['package_data'][PACKAGENAME].append('data/*.*') -package_info['package_data'][PACKAGENAME].append('data/performance_data/*') -package_info['package_data'][PACKAGENAME].append('data/sky_data/*') - -# Define entry points for command-line scripts -entry_points = {'console_scripts': []} - -if conf.has_section('entry_points'): - entry_point_list = conf.items('entry_points') - for entry_point in entry_point_list: - entry_points['console_scripts'].append('{0} = {1}'.format( - entry_point[0], entry_point[1])) - -# Include all .c files, recursively, including those generated by -# Cython, since we can not do this in MANIFEST.in with a "dynamic" -# directory name. -c_files = [] -for root, dirs, files in os.walk(PACKAGENAME): - for filename in files: - if filename.endswith('.c'): - c_files.append( - os.path.join( - os.path.relpath(root, PACKAGENAME), filename)) -package_info['package_data'][PACKAGENAME].extend(c_files) - -# Note that requires and provides should not be included in the call to -# ``setup``, since these are now deprecated. See this link for more details: -# https://groups.google.com/forum/#!topic/astropy-dev/urYO8ckB2uM - -setup(name=PACKAGENAME, - version=VERSION, - description=DESCRIPTION, - scripts=scripts, - install_requires=[s.strip() for s in metadata.get('install_requires', 'astropy').split(',')], - author=AUTHOR, - author_email=AUTHOR_EMAIL, - license=LICENSE, - url=URL, - long_description=LONG_DESCRIPTION, - cmdclass=cmdclassd, - zip_safe=False, - use_2to3=False, - entry_points=entry_points, - **package_info -) +setup() \ No newline at end of file diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..5cd62af --- /dev/null +++ b/tox.ini @@ -0,0 +1,83 @@ +[tox] +min_version = 4.0 +envlist = + py{39,310,311,312}-test + py10-test-oldestdeps + build_docs +isolated_build = True + +[gh-actions] +python = + 3.9: py39 + 3.10: py310 + 3.11: py311 + 3.12: py312 + +[gh-actions:env] +PLATFORM = + ubuntu-latest: linux + macos-latest: macos + + +[testenv] +# tox environments are constructed 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 + oldestdeps: with the oldest supported version of key dependencies + +# Pass through the following environment variables which may be needed for the CI +pass_env = + # A variable to tell tests we are on a CI system + CI + # Custom compiler locations (such as ccache) + CC + # Location of locales (needed by sphinx on some systems) + LOCALE_ARCHIVE + # If the user has set a LC override we should follow it + # (note LANG is automatically passed through by tox) + LC_ALL + +# Suppress display of matplotlib plots generated during docs build +set_env = + MPLBACKEND = agg + +# Run the tests in a temporary directory to make sure that we don't import +# the package from the source tree +change_dir = .tmp/{envname} + +deps = + oldestdeps: minimum_dependencies + pytest-cov + astropy + scipy + matplotlib + numpy + PyYAML + +# The following indicates which extras_require from setup.cfg will be installed +extras = + test + +commands_pre = + oldestdeps: minimum_dependencies gunagala --filename requirements-min.txt + oldestdeps: pip install -r requirements-min.txt + pip freeze + +commands = + pytest --pyargs gunagala --cov gunagala --cov-report xml:coverage.xml --cov-report term-missing {posargs} + +[testenv:build_docs] +description = invoke sphinx-build to build the HTML docs +change_dir = + docs +extras = + docs +commands = + sphinx-build -j auto --color -W --keep-going -b html -d _build/.doctrees . _build/html {posargs}