diff --git a/.gitignore b/.gitignore index f3f3da5..a8bf990 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Compiled files -*.py[co] +*.py[cod] *.a *.o *.so @@ -17,6 +17,7 @@ __pycache__ htmlcov .coverage MANIFEST +.ipynb_checkpoints # Sphinx docs/api @@ -33,6 +34,7 @@ docs/_build # Packages/installer info *.egg *.egg-info +*.eggs dist build eggs @@ -45,7 +47,9 @@ develop-eggs distribute-*.tar.gz # Other -.*.swp +.cache +.tox +.*.sw[op] *~ # Mac OSX diff --git a/.travis.yml b/.travis.yml index c3e66c1..147aa0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,23 +13,58 @@ addons: - dvipng python: - - 2.6 - 2.7 - 3.4 - 3.5 - # This is just for "egg_info". All other builds are explicitly given in the matrix + +# 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 - # overidden underneath. They are defined here in order to save having + # overridden underneath. They are defined here in order to save having # to repeat them for all configurations. - - NUMPY_VERSION=1.10 + + - NUMPY_VERSION=stable - ASTROPY_VERSION=stable - - CONDA_INSTALL='conda install -c astropy-ci-extras --yes' - - PIP_INSTALL='pip install' + - SETUP_CMD='test' + - PIP_DEPENDENCIES='' + + # For this package-template, we include examples of Cython modules, + # so Cython is required for testing. If your package does not include + # Cython code, you can set CONDA_DEPENDENCIES='' + - CONDA_DEPENDENCIES='Cython scipy' + + # 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='astopy-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 - SETUP_CMD='egg_info' + # Try all python versions with the latest numpy + - SETUP_CMD='test' matrix: include: @@ -45,71 +80,62 @@ matrix: # Try Astropy development version - python: 2.7 - env: ASTROPY_VERSION=development SETUP_CMD='test' + env: ASTROPY_VERSION=development - python: 3.5 - env: ASTROPY_VERSION=development SETUP_CMD='test' - - # Try all python versions with the latest numpy + env: ASTROPY_VERSION=development - python: 2.7 - env: SETUP_CMD='test' - - python: 3.4 - env: SETUP_CMD='test' + env: ASTROPY_VERSION=lts - python: 3.5 - env: SETUP_CMD='test' + env: ASTROPY_VERSION=lts + + # Python 3.3 doesn't have numpy 1.10 in conda, but can be put + # back into the main matrix once the numpy build is available in the + # astropy-ci-extras channel (or in the one provided in the + # CONDA_CHANNELS environmental variable). + + - python: 3.3 + env: SETUP_CMD='egg_info' + - python: 3.3 + env: SETUP_CMD='test' NUMPY_VERSION=1.9 # Try older numpy versions - python: 2.7 - env: NUMPY_VERSION=1.9 SETUP_CMD='test' + env: NUMPY_VERSION=1.10 - python: 2.7 - env: NUMPY_VERSION=1.8 SETUP_CMD='test' + env: NUMPY_VERSION=1.9 - python: 2.7 - env: NUMPY_VERSION=1.7 SETUP_CMD='test' - -before_install: - - # Use utf8 encoding. Should be default, but this is insurance against - # future changes - - export PYTHONIOENCODING=UTF8 + env: NUMPY_VERSION=1.8 + - python: 2.7 + env: NUMPY_VERSION=1.7 - # http://conda.pydata.org/docs/travis.html#the-travis-yml-file - - wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh; - - bash miniconda.sh -b -p $HOME/miniconda - - export PATH="$HOME/miniconda/bin:$PATH" - - hash -r - - conda config --set always_yes yes --set changeps1 no - - conda update -q conda - - conda info -a + # Try numpy pre-release + - python: 3.5 + env: NUMPY_VERSION=prerelease install: - # CONDA - - conda create --yes -n test -c astropy-ci-extras python=$TRAVIS_PYTHON_VERSION - - source activate test - - # CORE DEPENDENCIES - - if [[ $SETUP_CMD != egg_info ]]; then $CONDA_INSTALL numpy=$NUMPY_VERSION pytest pip Cython jinja2; fi - - if [[ $SETUP_CMD != egg_info ]]; then $PIP_INSTALL pytest-xdist; fi - - # ASTROPY - - if [[ $SETUP_CMD != egg_info ]] && [[ $ASTROPY_VERSION == development ]]; then $PIP_INSTALL git+http://github.com/astropy/astropy.git#egg=astropy; fi - - if [[ $SETUP_CMD != egg_info ]] && [[ $ASTROPY_VERSION == stable ]]; then $CONDA_INSTALL numpy=$NUMPY_VERSION astropy; fi - - # OPTIONAL DEPENDENCIES - # Here you can add any dependencies your package may have. You can use - # conda for packages available through conda, or pip for any other - # packages. You should leave the `numpy=$NUMPY_VERSION` in the `conda` - # install since this ensures Numpy does not get automatically upgraded. - - if [[ $SETUP_CMD != egg_info ]]; then $CONDA_INSTALL numpy=$NUMPY_VERSION scipy; fi - - if [[ $SETUP_CMD != egg_info ]]; then $PIP_INSTALL scipy; fi - - # DOCUMENTATION DEPENDENCIES - # build_sphinx needs sphinx and matplotlib (for plot_directive). Note that - # this matplotlib will *not* work with py 3.x, but our sphinx build is - # currently 2.7, so that's fine - - if [[ $SETUP_CMD == build_sphinx* ]]; then $CONDA_INSTALL numpy=$NUMPY_VERSION Sphinx matplotlib; fi - - # COVERAGE DEPENDENCIES - - if [[ $SETUP_CMD == 'test --coverage' ]]; then $PIP_INSTALL coverage==3.7.1 coveralls; fi + # 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 git://github.com/astropy/ci-helpers.git + - source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.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: - python setup.py $SETUP_CMD diff --git a/CHANGES.rst b/CHANGES.rst index c8313a3..6452a7c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,10 +1,22 @@ -1.1 (unreleased) ----------------- +1.0.5 (2016-08-16) +------------------ +- Updated to newest version of astropy package template. + +- Fixed median cleaning. There was a subtle bug that the crmask was defined as a unit8 + array. This was then used to clean the image, but this acted as indexes 0 and 1 rather than + a boolean array that was intended + +1.0.4 (2016-02-29) +------------------ - Fixed setup_requires so that it doesn't install astropy when using egg_info. - Pinned coverage version to 3.7.1. +- Removed dependence on endianness in tests + +- Fixed build issues on windows + 1.0.3 (2015-09-29) ------------------ diff --git a/MANIFEST.in b/MANIFEST.in index b9422b0..f6fbbd3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -15,8 +15,26 @@ prune build prune docs/_build prune docs/api -recursive-include astropy_helpers * -exclude astropy_helpers/.git -exclude astropy_helpers/.gitignore + +# the next few stanzas are for astropy_helpers. It's derived from the +# astropy_helpers/MANIFEST.in, but requires additional includes for the actual +# package directory and egg-info. + +include astropy_helpers/README.rst +include astropy_helpers/CHANGES.rst +include astropy_helpers/LICENSE.rst +recursive-include astropy_helpers/licenses * + +include astropy_helpers/ez_setup.py +include astropy_helpers/ah_bootstrap.py + +recursive-include astropy_helpers/astropy_helpers *.py *.pyx *.c *.h +recursive-include astropy_helpers/astropy_helpers.egg-info * +# include the sphinx stuff with "*" because there are css/html/rst/etc. +recursive-include astropy_helpers/astropy_helpers/sphinx * + +prune astropy_helpers/build +prune astropy_helpers/astropy_helpers/tests + global-exclude *.pyc *.o diff --git a/README.rst b/README.rst index 1ce03f1..de8edbd 100644 --- a/README.rst +++ b/README.rst @@ -52,7 +52,7 @@ There are some differences from original LA Cosmic: by a factor of ~90. The arrays always must be C-contiguous, thus all loops are y outer, x inner. -This follows the Pyfits convention. +This follows the astropy.io.fits (pyfits) convention. scipy is required for certain tests to pass, but the code itself does not depend on scipy. @@ -61,3 +61,4 @@ scipy. :target: https://travis-ci.org/astropy/astroscrappy .. image:: https://coveralls.io/repos/astropy/astroscrappy/badge.png :target: https://coveralls.io/r/astropy/astroscrappy + :alt: Travis Status diff --git a/TEMPLATE_CHANGES.md b/TEMPLATE_CHANGES.md new file mode 100644 index 0000000..275d950 --- /dev/null +++ b/TEMPLATE_CHANGES.md @@ -0,0 +1,148 @@ +This file keeps track of changes between tagged versions of the Astropy +package template, for the benefit of affiliated package maintainers. It can +be removed in affiliated packages. + +The changes below indicate what file the change was made in so that these can +be copied over manually if desired. + +1.1.3 (unreleased) +------------------ + +- Removed Python 2.6 tests from travis.yml file as astropy 1.2 no longer supports Python 2.6 [#183] + +1.1.2 (2016-07-02) +------------------ + +- Updated .travis.yml to show usage of SETUP_XVFB ci-helpers option. [#177] + +- Updated astropy-helpers to v1.2. [#180] + +- Fixed import of ``configparser`` in ``docs/conf.py``. [#180] + +1.1.1 (2016-06-03) +------------------ + +- Fixed the import of configparser on Python 3.5. [#172] + +- Updated ``ez_setup.py`` to the latest version. [#174] + +1.1 (2016-06-01) +---------------- + +- Fixed the import of example_mod.py in __init__.py to work properly on + Python 3. [#167] + +- Fixed the version import in conftest.py to work properly if version.py + hasn't been generated yet. [#167] + +- Switch to using container-based builds on Travis. [#133] + +- Entry points are now defined in the ``setup.cfg`` file. [#130] + +- Expanded the default .gitignore file. [#146] + +- Switch to using ci-helpers on Travis, and add example AppVeyor config file. + [#140] + +- Updated astropy-helpers to v1.1.2. [#147] + +- Remove ``adjust_compiler`` from ``setup.py`` (this is now dealt with in + astropy-helpers). [#154] + +- Update ``MANIFEST.in`` to avoid bundling temporary files in astropy-helpers + when releasing packages. [#154] + +- Updated ``ez_setup.py`` to the latest version. [#135] + +- Catch ``KeyError`` when setting up custom test headers in ``conftest.py``. + [#143] + +- Update ``.travis.yml`` to include testing with the LTS release of Astropy, + remove testing against Python 2.6, and update Numpy versions [#148, #168] + +- Add an example of how to include data in ``example_subpkg/setup_package.py``. + [#158] + +- Update ReadTheDocs domains. [#166] + +- Updated Sphinx Makefile. [#171] + +- Updated AppVeyor config to run on Python 2.7 and 3.5. [#170] + +In summary, the following files should be updated by affiliated packages: + +- ``.gitignore`` +- ``MANIFEST.in`` +- ``docs/Makefile`` +- ``ez_setup.py`` +- ``packagename/conftest.py`` +- ``setup.py`` +- ``setup.cfg`` (if using entry points) + +In addition, astropy-helpers should be updated to v1.1.2 and the +``ah_bootstrap.py`` file should be updated to match the version in +``astropy-helpers``. + +Finally, the following files have been updated, but don't necessarily need to +be updated since they will likely be heavily customized in affiliated packages: + +- ``.travis.yml`` +- ``appveyor.yml`` + +1.0 (2015-05-31) +---------------- + +- The instructions for the documentation have now been clarified to indicate + that packages do not *have* to include the documentation in a sub-folder of + ``docs`` (see updated note in ``docs/index.rst``). [#123] + +- Updated ``setup.cfg`` to enable ``doctest_plus`` by default. + +- Updated ``.travis.yml`` to: + + - Update apt-get package list + + - Add ``jinja2`` as a dependency to be installed with conda [#114] + + - Drop Python 3.2 testing [#114] + + - Drop Numpy 1.5 testing, and use Numpy 1.9 as a baseline [#114] + +- Updated ``MANIFEST.in`` to: + + - Recursively include *.pyx, *.c, and *.pxd files + + - Globally exclude *.pyc and *.o files + + - Include ``CHANGES.rst`` + +- Update ``docs/conf.py`` to import Sphinx extensions from + ``astropy_helpers`` instead of ``astropy``. [#119] + +- Added 'Powered by Astropy badge' to ``README.rst``. [#112] + +- Show how to add and remove packages from pytest header in + ``packagename/conftest.py``, and show how to show the package version + instead of the astropy version in the top line. + +- Minor documentation change in ``packagename/_astropy_init.py``. [#110] + +- Use setuptools entry_points for command line scripts (change in + ``setup.py``). [#116] + +- Updated ``astropy-helpers`` and ``ah_bootstrap.py`` to v1.0.2. + +- Remove requires and provides from setup.py. [#128] + +0.4.1 (2014-10-22) +------------------ + +- Changed order of exclusion in MANIFEST.in, excluding *.pyc *after* including + astropy-template + +- Updated astropy-helpers to v0.4.3 + +0.4 (2014-08-14) +---------------- + +- Initial tagged version, contains astropy-helpers v0.4.1 diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..856447c --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,52 @@ +# AppVeyor.com is a Continuous Integration service to build and run tests under +# Windows + +environment: + + global: + PYTHON: "C:\\conda" + MINICONDA_VERSION: "latest" + CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\ci-helpers\\appveyor\\windows_sdk.cmd" + PYTHON_ARCH: "64" # needs to be set for CMD_IN_ENV to succeed. If a mix + # of 32 bit and 64 bit builds are needed, move this + # to the matrix section. + + # For this package-template, we include examples of Cython modules, + # so Cython is required for testing. If your package does not include + # Cython code, you can set CONDA_DEPENDENCIES='' + CONDA_DEPENDENCIES: "Cython" + + # Conda 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: "astopy-ci-extras astropy" + + matrix: + + # We test Python 2.7 and 3.5 because 2.7 is the supported Python 2 + # release of Astropy and Python 3.5 is the latest Python 3 release. + + - PYTHON_VERSION: "2.7" + ASTROPY_VERSION: "stable" + NUMPY_VERSION: "stable" + + - PYTHON_VERSION: "3.5" + ASTROPY_VERSION: "stable" + NUMPY_VERSION: "stable" + +platform: + -x64 + +install: + - "git clone git://github.com/astropy/ci-helpers.git" + - "powershell ci-helpers/appveyor/install-miniconda.ps1" + - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" + - "activate test" + +# Not a .NET project, we build the package in the install step instead +build: false + +test_script: + - "%CMD_IN_ENV% python setup.py test" diff --git a/astroscrappy/astroscrappy.pyx b/astroscrappy/astroscrappy.pyx index 65e76ba..467bf28 100644 --- a/astroscrappy/astroscrappy.pyx +++ b/astroscrappy/astroscrappy.pyx @@ -338,8 +338,8 @@ def detect_cosmics(indat, inmask=None, float sigclip=4.5, float sigfrac=0.3, # otherwise clean the image and iterate if cleantype == 'median': # Unmasked median filter - cindices = crmask.nonzero() - cleanarr[cindices] = m5[cindices] + crinds = crmask > 0 + cleanarr[crinds] = m5[crinds] del m5 # Masked mean filter elif cleantype == 'meanmask': @@ -538,7 +538,7 @@ cdef void clean_medmask(float[:, ::1] cleanarr, bool[:, ::1] crmask, else: # else take the mean cleanarr[j, i] = cymedian(medarr, numpix) - #cleanarr[j, i] = PyMedian(medarr, numpix) + free(medarr) diff --git a/astroscrappy/tests/fake_data.py b/astroscrappy/tests/fake_data.py new file mode 100644 index 0000000..baf9109 --- /dev/null +++ b/astroscrappy/tests/fake_data.py @@ -0,0 +1,67 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from __future__ import (absolute_import, division, print_function, + unicode_literals) +import numpy as np + + +# Make a simple Gaussian function for testing purposes +def gaussian(image_shape, x0, y0, brightness, fwhm): + x = np.arange(image_shape[1]) + y = np.arange(image_shape[0]) + x2d, y2d = np.meshgrid(x, y) + + sig = fwhm / 2.35482 + + normfactor = brightness / 2.0 / np.pi * sig ** -2.0 + exponent = -0.5 * sig ** -2.0 + exponent *= (x2d - x0) ** 2.0 + (y2d - y0) ** 2.0 + + return normfactor * np.exp(exponent) + + +def make_fake_data(): + """ + Generate fake data that can be used to test the detection and cleaning algorithms + + Returns + ------- + imdata : numpy float array + Fake Image data + crmask : numpy boolean array + Boolean mask of locations of injected cosmic rays + """ + # Set a seed so that the tests are repeatable + np.random.seed(200) + + # Create a simulated image to use in our tests + imdata = np.zeros((1001, 1001), dtype=np.float32) + + # Add sky and sky noise + imdata += 200 + + # Add some fake sources + for i in range(100): + x = np.random.uniform(low=0.0, high=1001) + y = np.random.uniform(low=0.0, high=1001) + brightness = np.random.uniform(low=1000., high=30000.) + imdata += gaussian(imdata.shape, x, y, brightness, 3.5) + + # Add the poisson noise + imdata = np.float32(np.random.poisson(imdata)) + + # Add readnoise + imdata += np.random.normal(0.0, 10.0, size=(1001, 1001)) + + # Add 100 fake cosmic rays + cr_x = np.random.randint(low=5, high=995, size=100) + cr_y = np.random.randint(low=5, high=995, size=100) + + cr_brightnesses = np.random.uniform(low=1000.0, high=30000.0, size=100) + + imdata[cr_y, cr_x] += cr_brightnesses + imdata = imdata.astype('f4') + + # Make a mask where the detected cosmic rays should be + crmask = np.zeros((1001, 1001), dtype=np.bool) + crmask[cr_y, cr_x] = True + return imdata, crmask diff --git a/astroscrappy/tests/test_astroscrappy.py b/astroscrappy/tests/test_astroscrappy.py index c027013..9aa9a10 100644 --- a/astroscrappy/tests/test_astroscrappy.py +++ b/astroscrappy/tests/test_astroscrappy.py @@ -1,60 +1,13 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst from __future__ import (absolute_import, division, print_function, unicode_literals) -import numpy as np -from ..astroscrappy import detect_cosmics - -# Make a simple Gaussian function for testing purposes -def gaussian(image_shape, x0, y0, brightness, fwhm): - x = np.arange(image_shape[1]) - y = np.arange(image_shape[0]) - x2d, y2d = np.meshgrid(x, y) - - sig = fwhm / 2.35482 - - normfactor = brightness / 2.0 / np.pi * sig ** -2.0 - exponent = -0.5 * sig ** -2.0 - exponent *= (x2d - x0) ** 2.0 + (y2d - y0) ** 2.0 - - return normfactor * np.exp(exponent) - - -# Set a seed so that the tests are repeatable -np.random.seed(200) - -# Create a simulated image to use in our tests -imdata = np.zeros((1001, 1001), dtype=np.float32) -# Add sky and sky noise -imdata += 200 - -# Add some fake sources -for i in range(100): - x = np.random.uniform(low=0.0, high=1001) - y = np.random.uniform(low=0.0, high=1001) - brightness = np.random.uniform(low=1000., high=30000.) - imdata += gaussian(imdata.shape, x, y, brightness, 3.5) - -# Add the poisson noise -imdata = np.float32(np.random.poisson(imdata)) - -# Add readnoise -imdata += np.random.normal(0.0, 10.0, size=(1001, 1001)) - -# Add 100 fake cosmic rays -cr_x = np.random.randint(low=5, high=995, size=100) -cr_y = np.random.randint(low=5, high=995, size=100) - -cr_brightnesses = np.random.uniform(low=1000.0, high=30000.0, size=100) - -imdata[cr_y, cr_x] += cr_brightnesses -imdata = imdata.astype('f4') +from ..astroscrappy import detect_cosmics +from . import fake_data -# Make a mask where the detected cosmic rays should be -expected_crmask = np.zeros((1001, 1001), dtype=np.bool) -expected_crmask[cr_y, cr_x] = True def test_main(): + imdata, expected_crmask = fake_data.make_fake_data() # Because our image only contains single cosmics, turn off # neighbor detection. Also, our cosmic rays are high enough # contrast that we can turn our detection threshold up. diff --git a/astroscrappy/tests/test_cleaning.py b/astroscrappy/tests/test_cleaning.py new file mode 100644 index 0000000..45a9b26 --- /dev/null +++ b/astroscrappy/tests/test_cleaning.py @@ -0,0 +1,69 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from __future__ import (absolute_import, division, print_function, + unicode_literals) +from ..astroscrappy import detect_cosmics + +from . import fake_data + +# Get fake data to work on +imdata, crmask = fake_data.make_fake_data() + + +def test_median_clean(): + # Because our image only contains single cosmics, turn off + # neighbor detection. Also, our cosmic rays are high enough + # contrast that we can turn our detection threshold up. + _mask, clean = detect_cosmics(imdata, readnoise=10., gain=1.0, + sigclip=6, sigfrac=1.0, cleantype='median') + + assert (clean[crmask] != imdata[crmask]).sum() == crmask.sum() + + # Run it again on the clean data. We shouldn't find any new cosmic rays + _mask2, _clean2 = detect_cosmics(clean, readnoise=10., gain=1.0, + sigclip=6, sigfrac=1.0, cleantype='median') + assert _mask2.sum() == 0 + + +def test_medmask_clean(): + # Because our image only contains single cosmics, turn off + # neighbor detection. Also, our cosmic rays are high enough + # contrast that we can turn our detection threshold up. + _mask, clean = detect_cosmics(imdata, readnoise=10., gain=1.0, + sigclip=6, sigfrac=1.0, cleantype='medmask') + + assert (clean[crmask] != imdata[crmask]).sum() == crmask.sum() + + # Run it again on the clean data. We shouldn't find any new cosmic rays + _mask2, _clean2 = detect_cosmics(clean, readnoise=10., gain=1.0, + sigclip=6, sigfrac=1.0, cleantype='medmask') + assert _mask2.sum() == 0 + + +def test_meanmask_clean(): + # Because our image only contains single cosmics, turn off + # neighbor detection. Also, our cosmic rays are high enough + # contrast that we can turn our detection threshold up. + _mask, clean = detect_cosmics(imdata, readnoise=10., gain=1.0, + sigclip=6, sigfrac=1.0, cleantype='meanmask') + + assert (clean[crmask] != imdata[crmask]).sum() == crmask.sum() + + # Run it again on the clean data. We shouldn't find any new cosmic rays + _mask2, _clean2 = detect_cosmics(clean, readnoise=10., gain=1.0, + sigclip=6, sigfrac=1.0, cleantype='meanmask') + assert _mask2.sum() == 0 + + +def test_idw_clean(): + # Because our image only contains single cosmics, turn off + # neighbor detection. Also, our cosmic rays are high enough + # contrast that we can turn our detection threshold up. + _mask, clean = detect_cosmics(imdata, readnoise=10., gain=1.0, + sigclip=6, sigfrac=1.0, cleantype='idw') + + assert (clean[crmask] != imdata[crmask]).sum() == crmask.sum() + + # Run it again on the clean data. We shouldn't find any new cosmic rays + _mask2, _clean2 = detect_cosmics(clean, readnoise=10., gain=1.0, + sigclip=6, sigfrac=1.0, cleantype='idw') + assert _mask2.sum() == 0 diff --git a/astroscrappy/tests/test_cleanmedian.py b/astroscrappy/tests/test_cleanmedian.py deleted file mode 100644 index 04cae42..0000000 --- a/astroscrappy/tests/test_cleanmedian.py +++ /dev/null @@ -1,66 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -from __future__ import (absolute_import, division, print_function, - unicode_literals) -import numpy as np -from ..astroscrappy import detect_cosmics -imdata = np.array([ 2369, 2404, 2387, 2406, 2448, 2470, 2454, 2460, - 2494, 2491, 2448, 2411, 2413, 2393, 2408, 2378, - 2364, 2365, 2331, 2304, 2362, 2353, 2381, 2405, - 2458, 2437, 2426, 2442, 2484, 2418, 2444, 2444, - 2398, 2388, 2395, 2373, 2352, 2332, 2322, 2310, - 2338, 2353, 2370, 2396, 2367, 2432, 2419, 2433, - 2420, 2432, 2413, 2398, 2405, 2356, 2376, 2383, - 2338, 2331, 2290, 2284, 2302, 2337, 2329, 2364, - 2391, 2375, 2441, 2386, 2421, 2398, 2370, 2414, - 2407, 2349, 2391, 2360, 2326, 2335, 2336, 2290, - 2334, 2333, 2320, 2324, 2372, 2365, 2392, 2398, - 2361, 2367, 2348, 2357, 2361, 2345, 2335, 2310, - 2337, 2310, 2307, 2323, 2304, 2324, 2327, 2338, - 2365, 2338, 2336, 2319, 2357, 2378, 2391, 2345, - 2342, 2342, 2325, 2304, 2292, 2336, 2329, 2307, - 2309, 2287, 2309, 2283, 2344, 2318, 2312, 2334, - 2330, 2336, 2328, 2304, 2334, 2339, 2298, 2291, - 2321, 2315, 2306, 2320, 2308, 2293, 2317, 2333, - 2326, 2313, 2356, 2327, 2298, 2337, 2328, 2320, - 2316, 2297, 2313, 2332, 2310, 2307, 2319, 2302, - 2333, 2338, 2322, 2303, 2316, 2279, 2324, 2318, - 2290, 2322, 2325, 2342, 2336, 2292, 2301, 2299, - 2287, 2315, 2301, 2318, 2301, 2306, 2313, 2325, - 2313, 2313, 2294, 2302, 2309, 2306, 2333, 2327, - 2310, 2257, 2304, 2315, 2303, 2309, 2314, 2315, - 2331, 2321, 2310, 2307, 2317, 2299, 2324, 2315, - 2323, 2336, 2295, 2316, 2330, 2309, 2306, 2314, - 2272, 2347, 2319, 2310, 2301, 2320, 2321, 2408, - 2703, 2649, 2295, 2329, 2321, 2307, 2321, 2320, - 2312, 2298, 2312, 2311, 2298, 2301, 2296, 2317, - 2306, 2332, 2307, 2334, 2310, 2339, 2658, 2658, - 2643, 2660, 2523, 2322, 2284, 2322, 2322, 2338, - 2302, 2287, 2309, 2307, 2305, 2317, 2316, 2310, - 2306, 2306, 2344, 2310, 2285, 2308, 2421, 2679, - 2675, 2701, 2657, 2421, 2303, 2331, 2307, 2307, - 2317, 2297, 2304, 2319, 2327, 2300, 2331, 2338, - 2302, 2323, 2303, 2305, 2308, 2313, 2322, 2554, - 2654, 2670, 2678, 2638, 2316, 2302, 2322, 2299, - 2339, 2305, 2321, 2291, 2280, 2304, 2324, 2318, - 2282, 2281, 2301, 2306, 2295, 2284, 2288, 2313, - 2294, 2301, 2324, 2344, 2330, 2304, 2312, 2317, - 2326, 2306, 2326, 2318, 2329, 2318, 2300, 2287, - 2268, 2294, 2293, 2319, 2279, 2322, 2298, 2298, - 2330, 2296, 2326, 2331, 2310, 2303, 2314, 2329, - 2315, 2322, 2296, 2307, 2272, 2309, 2310, 2315, - 2325, 2314, 2264, 2293, 2312, 2305, 2308, 2318, - 2287, 2267, 2314, 2302, 2310, 2277, 2310, 2307, - 2304, 2315, 2314, 2313, 2318, 2309, 2315, 2305, - 2312, 2289, 2268, 2310, 2315, 2294, 2314, 2312, - 2286, 2311, 2305, 2320, 2299, 2293, 2283, 2302]).reshape(20,20) - -imdata = imdata.astype('f4') - -def test_medclean(): - mask1, _clean = detect_cosmics(imdata, fsmode='median', - cleantype='median', niter = 1) - - mask2, _clean = detect_cosmics(imdata, fsmode='median', cleantype='median', niter = 2) - - assert (mask1.sum() < mask2.sum()) - diff --git a/astroscrappy/utils/setup_package.py b/astroscrappy/utils/setup_package.py index 8204de5..3e1fba2 100644 --- a/astroscrappy/utils/setup_package.py +++ b/astroscrappy/utils/setup_package.py @@ -11,12 +11,30 @@ UTIL_DIR = os.path.relpath(os.path.dirname(__file__)) -CODELINES = """ +CODELINES = r""" import sys +import os from distutils.ccompiler import new_compiler +from distutils.sysconfig import customize_compiler ccompiler = new_compiler() +customize_compiler(ccompiler) ccompiler.add_library('gomp') -sys.exit(int(ccompiler.has_function('omp_get_num_threads'))) +has_omp_functions = ccompiler.has_function('omp_get_num_threads') +with open('openmp_check.c', 'w') as f: + f.write('#include\n') + f.write('int main()\n') + f.write('{\n') + f.write('printf("Hello World");\n') + f.write('}') +try: + ccompiler.compile(['openmp_check.c'], extra_postargs=['-fopenmp']) + fopenmp_flag_works = True +except: + fopenmp_flag_works = False +os.remove('openmp_check.c') +if os.path.exists('openmp_check.o'): + os.remove('openmp_check.o') +sys.exit(int(has_omp_functions & fopenmp_flag_works)) """ @@ -26,7 +44,7 @@ def check_openmp(): # OpenMP in MSVC 2008 (python 2.7) and MSVC 2015 (python 3.5+), # but not MSVC 2010 (python 3.4 and lower). major, minor = sys.version_info[:2] - has_openmp = not (major == 3 and minor < 5) + has_openmp = not (major == 3 and minor < 5) # Empty return tuple is to match the alternative check, below. return has_openmp, ("", "") else: diff --git a/docs/Makefile b/docs/Makefile index c797950..fb03f26 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -34,11 +34,11 @@ help: @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" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR) -rm -rf api + -rm -rf generated html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @@ -129,6 +129,5 @@ linkcheck: "or in $(BUILDDIR)/linkcheck/output.txt." doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." + @echo "Run 'python setup.py test' in the root directory to run doctests " \ + @echo "in the documentation." diff --git a/docs/conf.py b/docs/conf.py index fbd16d3..51850ae 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,6 +47,7 @@ except ImportError: from configparser import ConfigParser conf = ConfigParser() + conf.read([os.path.join(os.path.dirname(__file__), '..', 'setup.cfg')]) setup_cfg = dict(conf.items('metadata')) diff --git a/ez_setup.py b/ez_setup.py index 9dc2c87..fc22046 100644 --- a/ez_setup.py +++ b/ez_setup.py @@ -1,66 +1,59 @@ -#!python -"""Bootstrap setuptools installation +#!/usr/bin/env python -If you want to use setuptools in your package's setup.py, just include this -file in the same directory with it, and add this to the top of your setup.py:: - - from ez_setup import use_setuptools - use_setuptools() +""" +Setuptools bootstrapping installer. -If you want to require a specific version of setuptools, set a download -mirror, or use an alternate download directory, you can do so by supplying -the appropriate options to ``use_setuptools()``. +Maintained at https://github.com/pypa/setuptools/tree/bootstrap. -This file can also be run as a script to install or upgrade setuptools. +Run this script to install or upgrade setuptools. """ + import os import shutil import sys import tempfile -import tarfile +import zipfile import optparse import subprocess import platform +import textwrap +import contextlib +import json +import codecs from distutils import log +try: + from urllib.request import urlopen + from urllib.parse import urljoin +except ImportError: + from urllib2 import urlopen + from urlparse import urljoin + try: from site import USER_SITE except ImportError: USER_SITE = None -DEFAULT_VERSION = "1.4.2" -DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" +LATEST = object() +DEFAULT_VERSION = LATEST +DEFAULT_URL = "https://pypi.io/packages/source/s/setuptools/" +DEFAULT_SAVE_DIR = os.curdir + def _python_cmd(*args): + """ + Execute a command. + + Return True if the command succeeded. + """ args = (sys.executable,) + args return subprocess.call(args) == 0 -def _check_call_py24(cmd, *args, **kwargs): - res = subprocess.call(cmd, *args, **kwargs) - class CalledProcessError(Exception): - pass - if not res == 0: - msg = "Command '%s' return non-zero exit status %d" % (cmd, res) - raise CalledProcessError(msg) -vars(subprocess).setdefault('check_call', _check_call_py24) - -def _install(tarball, install_args=()): - # extracting the tarball - tmpdir = tempfile.mkdtemp() - log.warn('Extracting in %s', tmpdir) - old_wd = os.getcwd() - try: - os.chdir(tmpdir) - tar = tarfile.open(tarball) - _extractall(tar) - tar.close() - - # going in the directory - subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) - os.chdir(subdir) - log.warn('Now working in %s', subdir) +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): @@ -68,93 +61,159 @@ def _install(tarball, install_args=()): log.warn('See the error message above.') # exitcode will be 2 return 2 - finally: - os.chdir(old_wd) - shutil.rmtree(tmpdir) -def _build_egg(egg, tarball, to_dir): - # extracting the tarball +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) - tar = tarfile.open(tarball) - _extractall(tar) - tar.close() + with ContextualZipFile(filename) as archive: + archive.extractall() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) - - # building an egg - log.warn('Building a Setuptools egg in %s', to_dir) - _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) + yield finally: os.chdir(old_wd) shutil.rmtree(tmpdir) - # returning the result - log.warn(egg) - if not os.path.exists(egg): - raise IOError('Could not build the egg.') def _do_download(version, download_base, to_dir, download_delay): + """Download Setuptools.""" egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' % (version, sys.version_info[0], sys.version_info[1])) if not os.path.exists(egg): - tarball = download_setuptools(version, download_base, + archive = download_setuptools(version, download_base, to_dir, download_delay) - _build_egg(egg, tarball, to_dir) + _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: - del sys.modules['pkg_resources'] + _unload_pkg_resources() import setuptools setuptools.bootstrap_install_from = egg -def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, - to_dir=os.curdir, download_delay=15): - # making sure we use the absolute path +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. + """ + version = _resolve_version(version) to_dir = os.path.abspath(to_dir) - was_imported = 'pkg_resources' in sys.modules or \ - 'setuptools' in sys.modules + + # 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 - except ImportError: - return _do_download(version, download_base, to_dir, download_delay) - try: pkg_resources.require("setuptools>=" + version) + # a suitable version is already installed return - except pkg_resources.VersionConflict: - e = sys.exc_info()[1] - if was_imported: - sys.stderr.write( - "The required version of setuptools (>=%s) is not available,\n" - "and can't be installed while this script is running. Please\n" - "install a more recent version first, using\n" - "'easy_install -U setuptools'." - "\n\n(Currently using %r)\n" % (version, e.args[0])) - sys.exit(2) - else: - del pkg_resources, sys.modules['pkg_resources'] # reload ok - return _do_download(version, download_base, to_dir, - download_delay) + except ImportError: + # pkg_resources not available; setuptools is not installed; download + pass except pkg_resources.DistributionNotFound: - return _do_download(version, download_base, to_dir, - download_delay) + # 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. + Run the command to download target. + + If the command fails, clean up before re-raising the error. """ try: subprocess.check_call(cmd) @@ -163,115 +222,110 @@ def _clean_check(cmd, target): os.unlink(target) raise + def download_file_powershell(url, target): """ - Download the file at url to target using Powershell (which will validate - trust). Raise an exception if the command cannot complete. + 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', - "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(), + 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'] - devnull = open(os.path.devnull, 'wb') - try: + with open(os.path.devnull, 'wb') as devnull: try: subprocess.check_call(cmd, stdout=devnull, stderr=devnull) - except: + except Exception: return False - finally: - devnull.close() return True - download_file_powershell.viable = has_powershell + def download_file_curl(url, target): - cmd = ['curl', url, '--silent', '--output', target] + cmd = ['curl', url, '--location', '--silent', '--output', target] _clean_check(cmd, target) + def has_curl(): cmd = ['curl', '--version'] - devnull = open(os.path.devnull, 'wb') - try: + with open(os.path.devnull, 'wb') as devnull: try: subprocess.check_call(cmd, stdout=devnull, stderr=devnull) - except: + except Exception: return False - finally: - devnull.close() 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'] - devnull = open(os.path.devnull, 'wb') - try: + with open(os.path.devnull, 'wb') as devnull: try: subprocess.check_call(cmd, stdout=devnull, stderr=devnull) - except: + except Exception: return False - finally: - devnull.close() return True - download_file_wget.viable = has_wget + def download_file_insecure(url, target): - """ - Use Python to download the file, even though it cannot authenticate the - connection. - """ - try: - from urllib.request import urlopen - except ImportError: - from urllib2 import urlopen - src = dst = None + """Use Python to download the file, without connection authentication.""" + src = urlopen(url) try: - src = urlopen(url) - # Read/write all in one block, so we don't create a corrupt file - # if the download is interrupted. + # Read all the data in one block. data = src.read() - dst = open(target, "wb") - dst.write(data) finally: - if src: - src.close() - if dst: - dst.close() + 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 = [ + 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) - for dl in downloaders: - if dl.viable(): - return dl -def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, - to_dir=os.curdir, delay=15, - downloader_factory=get_best_downloader): - """Download setuptools from a specified location and return its filename +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 egg for download under the `download_base` URL (which should end + 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. @@ -279,11 +333,12 @@ def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, ``downloader_factory`` should be a function taking no arguments and returning a function for downloading a URL to a target. """ + version = _resolve_version(version) # making sure we use the absolute path to_dir = os.path.abspath(to_dir) - tgz_name = "setuptools-%s.tar.gz" % version - url = download_base + tgz_name - saveto = os.path.join(to_dir, tgz_name) + 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() @@ -291,73 +346,42 @@ def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, return os.path.realpath(saveto) -def _extractall(self, path=".", members=None): - """Extract all members from the archive to the current working - directory and set owner, modification time and permissions on - directories afterwards. `path' specifies a different directory - to extract to. `members' is optional and must be a subset of the - list returned by getmembers(). +def _resolve_version(version): + """ + Resolve LATEST version """ - import copy - import operator - from tarfile import ExtractError - directories = [] - - if members is None: - members = self - - for tarinfo in members: - if tarinfo.isdir(): - # Extract directories with a safe mode. - directories.append(tarinfo) - tarinfo = copy.copy(tarinfo) - tarinfo.mode = 448 # decimal for oct 0700 - self.extract(tarinfo, path) - - # Reverse sort directories. - if sys.version_info < (2, 4): - def sorter(dir1, dir2): - return cmp(dir1.name, dir2.name) - directories.sort(sorter) - directories.reverse() - else: - directories.sort(key=operator.attrgetter('name'), reverse=True) - - # Set correct owner, mtime and filemode on directories. - for tarinfo in directories: - dirpath = os.path.join(path, tarinfo.name) + if version is not LATEST: + return version + + meta_url = urljoin(DEFAULT_URL, '/pypi/setuptools/json') + resp = urlopen(meta_url) + with contextlib.closing(resp): try: - self.chown(tarinfo, dirpath) - self.utime(tarinfo, dirpath) - self.chmod(tarinfo, dirpath) - except ExtractError: - e = sys.exc_info()[1] - if self.errorlevel > 1: - raise - else: - self._dbg(1, "tarfile: %s" % e) + charset = resp.info().get_content_charset() + except Exception: + # Python 2 compat; assume UTF-8 + charset = 'UTF-8' + reader = codecs.getreader(charset) + doc = json.load(reader(resp)) + + return str(doc['info']['version']) def _build_install_args(options): """ - Build the arguments to 'python setup.py install' on the setuptools package + Build the arguments to 'python setup.py install' on the setuptools package. + + Returns list of command line arguments. """ - install_args = [] - if options.user_install: - if sys.version_info < (2, 6): - log.warn("--user requires Python 2.6 or later") - raise SystemExit(1) - install_args.append('--user') - return install_args + return ['--user'] if options.user_install else [] + def _parse_args(): - """ - Parse the command line for options - """ + """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 (requires Python 2.6 or later)') + help='install in user site package') parser.add_option( '--download-base', dest='download_base', metavar="URL", default=DEFAULT_URL, @@ -367,16 +391,35 @@ def _parse_args(): 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 main(version=DEFAULT_VERSION): - """Install or upgrade setuptools and EasyInstall""" + +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() - tarball = download_setuptools(download_base=options.download_base, - downloader_factory=options.downloader_factory) - return _install(tarball, _build_install_args(options)) + archive = download_setuptools(**_download_args(options)) + return _install(archive, _build_install_args(options)) if __name__ == '__main__': sys.exit(main()) diff --git a/packagename/__init__.py b/packagename/__init__.py new file mode 100644 index 0000000..fe502a6 --- /dev/null +++ b/packagename/__init__.py @@ -0,0 +1,15 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst + +""" +This is an Astropy affiliated package. +""" + +# Affiliated packages may add whatever they like to this file, but +# should keep this content at the top. +# ---------------------------------------------------------------------------- +from ._astropy_init import * +# ---------------------------------------------------------------------------- + +# For egg_info test builds to pass, put package imports here. +if not _ASTROPY_SETUP_: + from .example_mod import * diff --git a/packagename/conftest.py b/packagename/conftest.py new file mode 100644 index 0000000..0ab65db --- /dev/null +++ b/packagename/conftest.py @@ -0,0 +1,38 @@ +# this contains imports plugins that configure py.test for astropy tests. +# by importing them here in conftest.py they are discoverable by py.test +# no matter how it is invoked within the source tree. + +from astropy.tests.pytest_plugins import * + +## Uncomment the following line to treat all DeprecationWarnings as +## exceptions +# 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 affiliated 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/packagename/example_subpkg/data/.gitignore b/packagename/example_subpkg/data/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/packagename/example_subpkg/setup_package.py b/packagename/example_subpkg/setup_package.py new file mode 100644 index 0000000..7cd5579 --- /dev/null +++ b/packagename/example_subpkg/setup_package.py @@ -0,0 +1,6 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from __future__ import absolute_import + + +def get_package_data(): + return {'packagename.example_subpkg': ['data/*']} diff --git a/packagename/example_subpkg/tests/__init__.py b/packagename/example_subpkg/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packagename/utils/tests/__init__.py b/packagename/utils/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/setup.cfg b/setup.cfg index ea55e41..87a59bc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,4 +24,6 @@ author_email = cmccully@lcogt.net license = BSD edit_on_github = True github_project = astropy/astroscrappy -url = https://github.com/astropy/astroscrappy \ No newline at end of file +url = https://github.com/astropy/astroscrappy + +[entry_points] diff --git a/setup.py b/setup.py index 8457f63..4cf83e7 100755 --- a/setup.py +++ b/setup.py @@ -15,8 +15,8 @@ import __builtin__ as builtins builtins._ASTROPY_SETUP_ = True -from astropy_helpers.setup_helpers import ( - register_commands, adjust_compiler, get_debug_option, get_package_info) +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 from astropy_helpers.distutils_helpers import is_distutils_display_option @@ -26,6 +26,8 @@ from ConfigParser import ConfigParser except ImportError: from configparser import ConfigParser + + conf = ConfigParser() conf.read(['setup.cfg']) metadata = dict(conf.items('metadata')) @@ -47,7 +49,7 @@ builtins._ASTROPY_PACKAGE_NAME_ = PACKAGENAME # VERSION should be PEP386 compatible (http://www.python.org/dev/peps/pep-0386) -VERSION = '1.1.dev' +VERSION = '1.0.5' # Indicates if this version is a release version RELEASE = 'dev' not in VERSION @@ -60,10 +62,6 @@ # modify distutils' behavior. cmdclassd = register_commands(PACKAGENAME, VERSION, RELEASE) -# Adjust the compiler in case the default on this platform is to use a -# broken one. -adjust_compiler(PACKAGENAME) - # Freeze build information in version.py generate_version_py(PACKAGENAME, VERSION, RELEASE, get_debug_option(PACKAGENAME)) @@ -83,7 +81,12 @@ package_info['package_data'][PACKAGENAME].append('data/*') # Define entry points for command-line scripts -entry_points = {} +entry_points = {'console_scripts': []} + +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" @@ -102,7 +105,7 @@ if is_distutils_display_option(): setup_requires = [] else: - setup_requires=['numpy','cython'], + setup_requires = ['numpy','cython'], # 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