[Git][debian-gis-team/pyorbital][master] 9 commits: New upstream version 1.4.0

Antonio Valentino gitlab at salsa.debian.org
Sun Nov 11 10:27:15 GMT 2018


Antonio Valentino pushed to branch master at Debian GIS Project / pyorbital


Commits:
8c3639b1 by Antonio Valentino at 2018-11-11T08:29:36Z
New upstream version 1.4.0
- - - - -
eb3c9fd9 by Antonio Valentino at 2018-11-11T08:29:36Z
Update upstream source from tag 'upstream/1.4.0'

Update to upstream version '1.4.0'
with Debian dir 13faef6031c5156f44c3802c3f9767f74bfe4648
- - - - -
847cd35c by Antonio Valentino at 2018-11-11T09:27:54Z
New upstream release

- - - - -
15aa8988 by Antonio Valentino at 2018-11-11T09:28:47Z
Update copyright file

- - - - -
6a810594 by Antonio Valentino at 2018-11-11T10:11:07Z
Update dependencies

- - - - -
feb43e8f by Antonio Valentino at 2018-11-11T10:17:40Z
Build sphinx doc using Python 3

- - - - -
73a28221 by Antonio Valentino at 2018-11-11T10:17:47Z
Refresh all patches

- - - - -
fb7d8dc6 by Antonio Valentino at 2018-11-11T10:17:47Z
Skip test using xarray or dask

- - - - -
18714851 by Antonio Valentino at 2018-11-11T10:22:16Z
Set distribution to unstable

- - - - -


28 changed files:

- − .bumpversion.cfg
- + .gitattributes
- − .gitchangelog.rc
- .github/PULL_REQUEST_TEMPLATE.md
- .travis.yml
- + CHANGELOG.md
- MANIFEST.in
- + RELEASING.md
- debian/changelog
- debian/control
- debian/copyright
- debian/patches/0001-install-test-sub-package.patch
- + debian/patches/0002-Skip-tests-using-on-xarray-or-dask-if-they-are-not-a.patch
- debian/patches/series
- debian/rules
- doc/source/conf.py
- doc/source/index.rst
- pyorbital/__init__.py
- pyorbital/geoloc_instrument_definitions.py
- pyorbital/orbital.py
- pyorbital/tests/test_aiaa.py
- pyorbital/tests/test_geoloc.py
- pyorbital/tests/test_orbital.py
- pyorbital/tlefile.py
- pyorbital/version.py
- setup.cfg
- setup.py
- + versioneer.py


Changes:

=====================================
.bumpversion.cfg deleted
=====================================
@@ -1,7 +0,0 @@
-[bumpversion]
-current_version = 1.3.1
-commit = True
-tag = True
-
-[bumpversion:file:pyorbital/version.py]
-


=====================================
.gitattributes
=====================================
@@ -0,0 +1 @@
+pyorbital/version.py export-subst


=====================================
.gitchangelog.rc deleted
=====================================
@@ -1,190 +0,0 @@
-##
-## Format
-##
-##   ACTION: [AUDIENCE:] COMMIT_MSG [!TAG ...]
-##
-## Description
-##
-##   ACTION is one of 'chg', 'fix', 'new'
-##
-##       Is WHAT the change is about.
-##
-##       'chg' is for refactor, small improvement, cosmetic changes...
-##       'fix' is for bug fixes
-##       'new' is for new features, big improvement
-##
-##   AUDIENCE is optional and one of 'dev', 'usr', 'pkg', 'test', 'doc'
-##
-##       Is WHO is concerned by the change.
-##
-##       'dev'  is for developpers (API changes, refactors...)
-##       'usr'  is for final users (UI changes)
-##       'pkg'  is for packagers   (packaging changes)
-##       'test' is for testers     (test only related changes)
-##       'doc'  is for doc guys    (doc only changes)
-##
-##   COMMIT_MSG is ... well ... the commit message itself.
-##
-##   TAGs are additionnal adjective as 'refactor' 'minor' 'cosmetic'
-##
-##       They are preceded with a '!' or a '@' (prefer the former, as the
-##       latter is wrongly interpreted in github.) Commonly used tags are:
-##
-##       'refactor' is obviously for refactoring code only
-##       'minor' is for a very meaningless change (a typo, adding a comment)
-##       'cosmetic' is for cosmetic driven change (re-indentation, 80-col...)
-##       'wip' is for partial functionality but complete subfunctionality.
-##
-## Example:
-##
-##   new: usr: support of bazaar implemented
-##   chg: re-indentend some lines !cosmetic
-##   new: dev: updated code to be compatible with last version of killer lib.
-##   fix: pkg: updated year of licence coverage.
-##   new: test: added a bunch of test around user usability of feature X.
-##   fix: typo in spelling my name in comment. !minor
-##
-##   Please note that multi-line commit message are supported, and only the
-##   first line will be considered as the "summary" of the commit message. So
-##   tags, and other rules only applies to the summary.  The body of the commit
-##   message will be displayed in the changelog without reformatting.
-
-
-##
-## ``ignore_regexps`` is a line of regexps
-##
-## Any commit having its full commit message matching any regexp listed here
-## will be ignored and won't be reported in the changelog.
-##
-ignore_regexps = [
-        r'@minor', r'!minor',
-        r'@cosmetic', r'!cosmetic',
-        r'@refactor', r'!refactor',
-        r'@wip', r'!wip',
-        r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[p|P]kg:',
-        r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[d|D]ev:',
-        r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$',
-  ]
-
-
-## ``section_regexps`` is a list of 2-tuples associating a string label and a
-## list of regexp
-##
-## Commit messages will be classified in sections thanks to this. Section
-## titles are the label, and a commit is classified under this section if any
-## of the regexps associated is matching.
-##
-section_regexps = [
-    ('New', [
-	r'^[nN]ew\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
-     ]),
-    ('Changes', [
-        r'^[cC]hg\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
-     ]),
-    ('Fix', [
-        r'^[fF]ix\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
-     ]),
-
-    ('Other', None ## Match all lines
-     ),
-
-]
-
-
-## ``body_process`` is a callable
-##
-## This callable will be given the original body and result will
-## be used in the changelog.
-##
-## Available constructs are:
-##
-##   - any python callable that take one txt argument and return txt argument.
-##
-##   - ReSub(pattern, replacement): will apply regexp substitution.
-##
-##   - Indent(chars="  "): will indent the text with the prefix
-##     Please remember that template engines gets also to modify the text and
-##     will usually indent themselves the text if needed.
-##
-##   - Wrap(regexp=r"\n\n"): re-wrap text in separate paragraph to fill 80-Columns
-##
-##   - noop: do nothing
-##
-##   - ucfirst: ensure the first letter is uppercase.
-##     (usually used in the ``subject_process`` pipeline)
-##
-##   - final_dot: ensure text finishes with a dot
-##     (usually used in the ``subject_process`` pipeline)
-##
-##   - strip: remove any spaces before or after the content of the string
-##
-## Additionally, you can `pipe` the provided filters, for instance:
-#body_process = Wrap(regexp=r'\n(?=\w+\s*:)') | Indent(chars="  ")
-#body_process = Wrap(regexp=r'\n(?=\w+\s*:)')
-body_process = noop
-
-
-## ``subject_process`` is a callable
-##
-## This callable will be given the original subject and result will
-## be used in the changelog.
-##
-## Available constructs are those listed in ``body_process`` doc.
-subject_process = (strip |
-    ReSub(r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n@]*)(@[a-z]+\s+)*$', r'\4') |
-    ucfirst | final_dot)
-
-
-## ``tag_filter_regexp`` is a regexp
-##
-## Tags that will be used for the changelog must match this regexp.
-##
-tag_filter_regexp = r'^v[0-9]+\.[0-9]+(\.[0-9]+)?$'
-
-
-## ``unreleased_version_label`` is a string
-##
-## This label will be used as the changelog Title of the last set of changes
-## between last valid tag and HEAD if any.
-unreleased_version_label = "%%version%% (unreleased)"
-
-
-## ``output_engine`` is a callable
-##
-## This will change the output format of the generated changelog file
-##
-## Available choices are:
-##
-##   - rest_py
-##
-##        Legacy pure python engine, outputs ReSTructured text.
-##        This is the default.
-##
-##   - mustache(<template_name>)
-##
-##        Template name could be any of the available templates in
-##        ``templates/mustache/*.tpl``.
-##        Requires python package ``pystache``.
-##        Examples:
-##           - mustache("markdown")
-##           - mustache("restructuredtext")
-##
-##   - makotemplate(<template_name>)
-##
-##        Template name could be any of the available templates in
-##        ``templates/mako/*.tpl``.
-##        Requires python package ``mako``.
-##        Examples:
-##           - makotemplate("restructuredtext")
-##
-output_engine = rest_py
-#output_engine = mustache("restructuredtext")
-#output_engine = mustache("markdown")
-#output_engine = makotemplate("restructuredtext")
-
-
-## ``include_merges`` is a boolean
-##
-## This option tells git-log whether to include merge commits in the log.
-## The default is to include them.
-include_merges = True


=====================================
.github/PULL_REQUEST_TEMPLATE.md
=====================================
@@ -1,9 +1,10 @@
-<!-- Please make the PR against the `develop` branch. -->
+<!-- Please make the PR against the `master` branch. -->
 
 <!-- Describe what your PR does, and why -->
 
  - [ ] Closes #xxxx <!-- remove if there is no corresponding issue, which should only be the case for minor changes -->
  - [ ] Tests added <!-- for all bug fixes or enhancements -->
  - [ ] Tests passed <!-- for all non-documentation changes) -->
- - [ ] Passes ``git diff origin/develop **/*py | flake8 --diff`` <!-- remove if you did not edit any Python files -->
+ - [ ] Passes ``git diff origin/master **/*py | flake8 --diff`` <!-- remove if you did not edit any Python files -->
  - [ ] Fully documented <!-- remove if this change should not be visible to users, e.g., if it is an internal clean-up, or if this is part of a larger project that will be documented later -->
+1
\ No newline at end of file


=====================================
.travis.yml
=====================================
@@ -1,9 +1,9 @@
 language: python
 python:
 - '2.7'
-- "3.5"
 - "3.6"
 install:
+- pip install dask[array] xarray
 - pip install .
 - pip install coveralls
 script: coverage run --source=pyorbital setup.py test


=====================================
CHANGELOG.md
=====================================
@@ -0,0 +1,26 @@
+## Version 1.4.0 (2018/10/23)
+
+### Issues Closed
+
+* [Issue 36](https://github.com/pytroll/pyorbital/issues/36) - Issue(s) with get_next_passes
+* [Issue 34](https://github.com/pytroll/pyorbital/issues/34) - Get root secant converging to wrong solution ([PR 35](https://github.com/pytroll/pyorbital/pull/35))
+* [Issue 30](https://github.com/pytroll/pyorbital/issues/30) - get_observer_look turns xarray.DataArray objects into dask.array objects
+* [Issue 29](https://github.com/pytroll/pyorbital/issues/29) - URL error
+* [Issue 27](https://github.com/pytroll/pyorbital/issues/27) - satellite.get_lonlatalt(now) returns wrong longitude with numpy < 1.11
+* [Issue 18](https://github.com/pytroll/pyorbital/issues/18) - Sun-satellite angle ranges are not consistent
+
+In this release 6 issues were closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 39](https://github.com/pytroll/pyorbital/pull/39) - Bugfix python3
+* [PR 35](https://github.com/pytroll/pyorbital/pull/35) - Use Scipy brentq method instead of secant method to perform root-finding ([34](https://github.com/pytroll/pyorbital/issues/34))
+
+#### Features added
+
+* [PR 37](https://github.com/pytroll/pyorbital/pull/37) - Switch to versioneer
+* [PR 33](https://github.com/pytroll/pyorbital/pull/33) - Remove Develop branch
+
+In this release 4 pull requests were closed.


=====================================
MANIFEST.in
=====================================
@@ -3,3 +3,5 @@ recursive-include doc/source *
 include LICENSE.txt
 include MANIFEST.in
 include README
+include versioneer.py
+include pyorbital/version.py


=====================================
RELEASING.md
=====================================
@@ -0,0 +1,27 @@
+# Releasing Pyorbital
+
+prerequisites: `pip install setuptools twine`
+
+1. checkout master
+2. pull from repo
+3. run the unittests
+4. run `loghub` and update the `CHANGELOG.md` file:
+
+```
+loghub pytroll/pyorbital -u <username> -st v0.8.0 -plg bug "Bugs fixed" -plg enhancement "Features added" -plg documentation "Documentation changes"
+```
+
+Don't forget to commit!
+
+5. Create a tag with the new version number, starting with a 'v', eg:
+
+```
+git tag v0.22.45
+```
+
+See [semver.org](http://semver.org/) on how to write a version number.
+
+
+
+6. push changes to github `git push --follow-tags`
+7. Verify travis tests passed and deployed sdist and wheel to PyPI


=====================================
debian/changelog
=====================================
@@ -1,11 +1,24 @@
-pyorbital (1.3.1-2) UNRELEASED; urgency=medium
+pyorbital (1.4.0-1) unstable; urgency=medium
 
-  * Team upload.
+  [ Bas Couwenberg ]
   * Drop ancient X-Python{,3}-Version fields.
   * Bump Standards-Version to 4.2.1, no changes.
   * Update watch file to limit matches to archive path.
 
- -- Bas Couwenberg <sebastic at debian.org>  Sun, 06 May 2018 09:16:37 +0200
+  [ Antonio Valentino ]
+  * New upstream release.
+  * Update copyright file.
+  * debian/control
+    - add dependency from scipy
+    - recommend dask and xarray
+  * debian/rules
+    - duild sphinx doc using Python 3
+  * debian/patches
+    - refresh all patches.
+    - new 0002-Skip-tests-using-on-xarray-or-dask-if-they-are-not-a.patch.
+      Skip tests using dask or xarray if they are not available.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it>  Sun, 11 Nov 2018 10:21:51 +0000
 
 pyorbital (1.3.1-1) unstable; urgency=medium
 


=====================================
debian/control
=====================================
@@ -11,8 +11,12 @@ Build-Depends: debhelper (>= 11),
                python3-all,
                python-numpy,
                python3-numpy,
+	       python-scipy,
+	       python3-scipy,
                python-sphinx,
-               python3-sphinx
+               python3-sphinx,
+               python3-dask,
+	       python3-xarray
 Standards-Version: 4.2.1
 Vcs-Browser: https://salsa.debian.org/debian-gis-team/pyorbital
 Vcs-Git: https://salsa.debian.org/debian-gis-team/pyorbital.git
@@ -23,7 +27,8 @@ Architecture: all
 Depends: ${shlibs:Depends},
          ${python:Depends},
          ${misc:Depends},
-         python-numpy
+         python-numpy,
+	 python-scipy
 Suggests: python-pyorbital-doc
 Description: Orbital and astronomy computations in Python 2
  Python package for computing orbital parameters from TLE
@@ -38,7 +43,10 @@ Architecture: all
 Depends: ${shlibs:Depends},
          ${python3:Depends},
          ${misc:Depends},
-         python3-numpy
+         python3-numpy,
+	 python3-scipy
+Recommends: python3-dask,
+	    python3-xarray
 Suggests: python-pyorbital-doc
 Description: Orbital and astronomy computations in Python 3
  Python package for computing orbital parameters from TLE


=====================================
debian/copyright
=====================================
@@ -3,15 +3,20 @@ Upstream-Name: pyorbital
 Source: https://github.com/pytroll/pyorbital
 
 Files: *
-Copyright: 2011-2015, 2017, Esben S. Nielsen <esn at dmi.dk>
-           2011-2015,       Panu Lahtinen <panu.lahtinen at fmi.fi
+Copyright: 2013-2018,       PyTroll Community
+           2011-2018,       Esben S. Nielsen <esn at dmi.dk>
+           2011-2018,       Panu Lahtinen <panu.lahtinen at fmi.fi
            2011, 2014,      SMHI
            2012-2015,       Adam Dybbroe <adam.dybbroe at smhi.se>
-           2012-2015, 2017, Martin Raspaud <martin.raspaud at smhi.se>
+           2011-2018,       Martin Raspaud <martin.raspaud at smhi.se>
            2012-2014,       The Pytroll crew
            2013-2015, 2017, Mikhail Itkin <itkin.m at gmail.com>
 License: GPL-3+
 
+Files: versioneer.py
+Copyright: 2018, Brian Warner
+License: CC0-1.0
+
 Files: debian/*
 Copyright: 2014, Antonio Valentino <antonio.valentino at tiscali.it>
 License: GPL-3+
@@ -32,3 +37,15 @@ License: GPL-3+
  .
  On Debian systems, the complete text of the GNU General
  Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
+
+License: CC0-1.0
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+ .
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
+ .
+ On Debian systems, the complete text of the CC0 1.0 Universal license can be
+ found in ‘/usr/share/common-licenses/CC0-1.0’.
+


=====================================
debian/patches/0001-install-test-sub-package.patch
=====================================
@@ -7,16 +7,16 @@ Subject: install test sub-package
  1 file changed, 2 insertions(+), 1 deletion(-)
 
 diff --git a/setup.py b/setup.py
-index 41ab292..c3747b9 100644
+index a5410ef..7575249 100644
 --- a/setup.py
 +++ b/setup.py
 @@ -41,7 +41,8 @@ setup(name='pyorbital',
-       url="https://github.com/mraspaud/pyorbital",
+       url="https://github.com/pytroll/pyorbital",
        test_suite='pyorbital.tests.suite',
-       package_dir = {'pyorbital': 'pyorbital'},
--      packages = ['pyorbital'],
-+      packages = ['pyorbital', 'pyorbital.tests'],
+       package_dir={'pyorbital': 'pyorbital'},
+-      packages=['pyorbital'],
++      packages=['pyorbital', 'pyorbital.tests'],
 +      package_data={'pyorbital.tests': ['aiaa_results', '*.TLE']},
-       install_requires=['numpy>=1.6.0,!=1.14.0'],
+       install_requires=['numpy>=1.11.0,!=1.14.0', 'scipy'],
        zip_safe=False,
        )


=====================================
debian/patches/0002-Skip-tests-using-on-xarray-or-dask-if-they-are-not-a.patch
=====================================
@@ -0,0 +1,37 @@
+From: Antonio Valentino <antonio.valentino at tiscali.it>
+Date: Sun, 11 Nov 2018 11:06:11 +0100
+Subject: Skip tests using on xarray or dask if they are not available
+
+---
+ pyorbital/tests/test_orbital.py | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/pyorbital/tests/test_orbital.py b/pyorbital/tests/test_orbital.py
+index 2ed1a24..0762eea 100644
+--- a/pyorbital/tests/test_orbital.py
++++ b/pyorbital/tests/test_orbital.py
+@@ -177,6 +177,7 @@ class TestGetObserverLook(unittest.TestCase):
+         np.testing.assert_allclose(azi, self.exp_azi)
+         np.testing.assert_allclose(elev, self.exp_elev)
+ 
++    @unittest.skipIf(not orbital.has_dask, 'dask not available')
+     def test_basic_dask(self):
+         """Test with dask array inputs"""
+         from pyorbital import orbital
+@@ -193,6 +194,7 @@ class TestGetObserverLook(unittest.TestCase):
+         np.testing.assert_allclose(azi.compute(), self.exp_azi)
+         np.testing.assert_allclose(elev.compute(), self.exp_elev)
+ 
++    @unittest.skipIf(not orbital.has_xarray, 'xarray not available')
+     def test_xarray_with_numpy(self):
+         """Test with xarray DataArray with numpy array as inputs"""
+         from pyorbital import orbital
+@@ -212,6 +214,8 @@ class TestGetObserverLook(unittest.TestCase):
+         np.testing.assert_allclose(azi.data, self.exp_azi)
+         np.testing.assert_allclose(elev.data, self.exp_elev)
+ 
++    @unittest.skipIf(not orbital.has_xarray or not orbital.has_dask,
++                     'xarray and/or dask not available')
+     def test_xarray_with_dask(self):
+         """Test with xarray DataArray with dask array as inputs"""
+         from pyorbital import orbital


=====================================
debian/patches/series
=====================================
@@ -1 +1,2 @@
 0001-install-test-sub-package.patch
+0002-Skip-tests-using-on-xarray-or-dask-if-they-are-not-a.patch


=====================================
debian/rules
=====================================
@@ -12,10 +12,12 @@ export PYBUILD_NAME=pyorbital
 	dh $@ --with python2,python3,sphinxdoc --buildsystem=pybuild
 
 
+override_dh_auto_build: export http_proxy=127.0.0.1:9
+override_dh_auto_build: export https_proxy=127.0.0.1:9
 override_dh_auto_build:
 	dh_auto_build --buildsystem=pybuild
 ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
-	$(MAKE) -C doc html
+	PYTHONPATH=. python3 -m sphinx -N -bhtml doc/source/ doc/build/html # HTML generator
 endif
 
 


=====================================
doc/source/conf.py
=====================================
@@ -17,10 +17,10 @@ import os
 # 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('.'))
+
 sys.path.insert(0, os.path.abspath('../../'))
 sys.path.insert(0, os.path.abspath('../../pyorbital'))
-from pyorbital.version import __version__
+from pyorbital import __version__  # noqa
 
 # -- General configuration -----------------------------------------------------
 
@@ -45,16 +45,16 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'pyorbital'
-copyright = u'2012-2015, The Pytroll crew'
+copyright = u'2012-2015, 2018, The Pytroll crew'
 
 # 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.
 #
+# The short X.Y version.
+version = __version__.split('+')[0]
 # The full version, including alpha/beta/rc tags.
 release = __version__
-# The short X.Y version.
-version = ".".join(release.split(".")[:2])
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


=====================================
doc/source/index.rst
=====================================
@@ -28,8 +28,8 @@ Pyorbital has a module for parsing NORAD TLE-files
 
 If no path is given pyorbital tries to read the earth observation TLE-files from celestrak.com
     
-Computing satellite postion
----------------------------
+Computing satellite position
+----------------------------
 The orbital module enables computation of satellite position and velocity at a specific time:
 
     >>> from pyorbital.orbital import Orbital
@@ -56,7 +56,7 @@ Use actual TLEs to increase accuracy
     >>> orb.get_lonlatalt(dtobj)
     (152.11564698762811, 20.475251739329622, 829.37355785502211)
 
-But since we are interesting knowing the position of the Suomi-NPP more than
+But since we are interested in knowing the position of the Suomi-NPP more than
 two and half years from now (September 26, 2017) we can not rely on the current
 TLEs, but rather need a TLE closer to the time of interest:
 


=====================================
pyorbital/__init__.py
=====================================
@@ -20,6 +20,9 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import numpy as np
+from .version import get_versions
+__version__ = get_versions()['version']
+del get_versions
 
 
 def dt2np(utc_time):


=====================================
pyorbital/geoloc_instrument_definitions.py
=====================================
@@ -1,12 +1,13 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2013, 2014, 2015, 2017 Martin Raspaud
+# Copyright (c) 2013 - 2018 PyTroll Community
 
 # Author(s):
 
 #   Martin Raspaud <martin.raspaud at smhi.se>
 #   Mikhail Itkin <itkin.m at gmail.com>
+#   Adam Dybbroe <adam.dybbroe at smhi.se>
 
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -141,15 +142,21 @@ def avhrr_40_geom(scans_nb):
 
 
 def viirs(scans_nb, scan_indices=slice(0, None),
-          chn_pixels=6400, scan_lines=32):
+          chn_pixels=6400, scan_lines=32, scan_step=1):
     """Describe VIIRS instrument geometry, I-band by default.
     VIIRS scans several lines simultaneously (there are 16 detectors for each
     M-band, 32 detectors for each I-band) so the scan angles (and times) are
     two-dimensional arrays, contrary to AVHRR for example.
+
+    scan_step: The increment in number of scans. E.g. if scan_step is 100 and
+               the number of scans (scans_nb) is 10 then these 10 scans are
+               distributed over the swath so that between each scan there are
+               99 emtpy (excluded) scans
+
     """
 
     entire_width = np.arange(chn_pixels)
-    scan_points = entire_width[scan_indices]
+    scan_points = entire_width[scan_indices.astype('int')]
     scan_pixels = len(scan_points)
 
     ''' initial angle 55.84 deg replaced with 56.28 deg found in
@@ -167,9 +174,10 @@ def viirs(scans_nb, scan_indices=slice(0, None),
     npp = np.tile(scan, [scans_nb, 1]).T
 
     # from the timestamp in the filenames, a granule takes 1:25.400 to record
-    # (85.4 seconds) so 1.779166667 would be the duration of 1 scanline
-    # dividing the duration of a single scan by a width of 6400 pixels results
-    # in 0.0002779947917 seconds for each column of 32 pixels in the scanline
+    # (85.4 seconds) so 1.779166667 would be the duration of 1 scanline (48
+    # scans per granule) dividing the duration of a single scan by a width of
+    # 6400 pixels results in 0.0002779947917 seconds for each column of 32
+    # pixels in the scanline
 
     # the individual times per pixel are probably wrong, unless the scanning
     # behaves the same as for AVHRR, The VIIRS sensor rotates to allow internal
@@ -177,10 +185,13 @@ def viirs(scans_nb, scan_indices=slice(0, None),
     # always moves in the same direction.  more info @
     # http://www.eoportal.org/directory/pres_NPOESSNationalPolarorbitingOperationalEnvironmentalSatelliteSystem.html
 
-    offset = np.arange(scans_nb) * 1.779166667
-    times = (np.tile(scan_points * 0.0002779947917,
-                     [np.int(scan_lines), np.int(scans_nb)])
-             + np.expand_dims(offset, 1))
+    SEC_EACH_SCANCOLUMN = 0.0002779947917
+    sec_scan_duration = 1.779166667
+    times = np.tile(scan_points * SEC_EACH_SCANCOLUMN,
+                    [np.int(scans_nb*scan_lines), 1])
+    offset = np.repeat(np.arange(scans_nb) *
+                       sec_scan_duration*scan_step, scan_lines)
+    times += np.expand_dims(offset, 1)
 
     # build the scan geometry object
     return ScanGeometry(npp, times)
@@ -269,9 +280,9 @@ def mhs(scans_nb, edges_only=False):
     """
 
     scan_len = 90  # 90 samples per scan
-    scan_rate = 8/3.  # single scan, seconds
+    scan_rate = 8 / 3.  # single scan, seconds
     scan_angle = -49.444  # swath, degrees
-    sampling_interval = (8/3.-1)/90.  # single view, seconds
+    sampling_interval = (8 / 3. - 1) / 90.  # single view, seconds
 
     if edges_only:
         scan_points = np.array([0, scan_len - 1])
@@ -325,7 +336,7 @@ def hirs4(scans_nb, edges_only=False):
     scan_len = 56  # 56 samples per scan
     scan_rate = 6.4  # single scan, seconds
     scan_angle = -49.5  # swath, degrees
-    sampling_interval = abs(scan_rate)/scan_len  # single view, seconds
+    sampling_interval = abs(scan_rate) / scan_len  # single view, seconds
 
     if edges_only:
         scan_points = np.array([0, scan_len - 1])
@@ -376,7 +387,7 @@ def atms(scans_nb, edges_only=False):
     """
 
     scan_len = 96  # 96 samples per scan
-    scan_rate = 8/3.  # single scan, seconds
+    scan_rate = 8 / 3.  # single scan, seconds
     scan_angle = -52.7  # swath, degrees
     sampling_interval = 18e-3  # single view, seconds
 
@@ -403,3 +414,85 @@ def atms(scans_nb, edges_only=False):
 def atms_edge_geom(scans_nb):
     # we take only edge pixels
     return atms(scans_nb, edges_only=True)
+
+################################################################
+#
+#   OLCI
+#
+################################################################
+
+
+def olci(scans_nb, scan_points=None):
+    """Definition of the OLCI instrument.
+
+    Source: Sentinel-3 OLCI Coverage
+    https://sentinel.esa.int/web/sentinel/user-guides/sentinel-3-olci/coverage
+    """
+
+    if scan_points is None:
+        scan_len = 4000  # samples per scan
+        scan_points = np.arange(4000)
+    else:
+        scan_len = len(scan_points)
+    # scan_rate = 0.044  # single scan, seconds
+    scan_angle_west = 46.5  # swath, degrees
+    scan_angle_east = -22.1  # swath, degrees
+    # sampling_interval = 18e-3  # single view, seconds
+    # build the olci instrument scan line angles
+    scanline_angles = np.linspace(np.deg2rad(scan_angle_west),
+                                  np.deg2rad(scan_angle_east), scan_len)
+    inst = np.vstack((scanline_angles, np.zeros(scan_len,)))
+
+    inst = np.tile(inst[:, np.newaxis, :], [1, np.int(scans_nb), 1])
+
+    # building the corresponding times array
+    # times = (np.tile(scan_points * 0.000025 + 0.0025415, [scans_nb, 1])
+    #         + np.expand_dims(offset, 1))
+
+    times = np.tile(np.zeros_like(scanline_angles), [np.int(scans_nb), 1])
+    # if apply_offset:
+    #     offset = np.arange(np.int(scans_nb)) * frequency
+    #     times += np.expand_dims(offset, 1)
+
+    return ScanGeometry(inst, times)
+
+
+def ascat(scan_nb, scan_points=None):
+    """ASCAT make two scans one to the left and one to the right of the
+    sub-satellite track.
+
+    """
+
+    if scan_points is None:
+        scan_len = 42  # samples per scan
+        scan_points = np.arange(42)
+    else:
+        scan_len = len(scan_points)
+
+    scan_angle_inner = -25.0  # swath, degrees
+    scan_angle_outer = -53.0  # swath, degrees
+    scan_rate = 3.74747474747  # single scan, seconds
+    if scan_len < 2:
+        raise ValueError("Need at least two scan points!")
+
+    sampling_interval = scan_rate / float(np.max(scan_points) + 1)
+
+    # build the Metop/ascat instrument scan line angles
+    scanline_angles_one = np.linspace(-np.deg2rad(scan_angle_outer),
+                                      -np.deg2rad(scan_angle_inner), 21)
+    scanline_angles_two = np.linspace(np.deg2rad(scan_angle_inner),
+                                      np.deg2rad(scan_angle_outer), 21)
+
+    scan_angles = np.concatenate(
+        [scanline_angles_one, scanline_angles_two])[scan_points]
+
+    inst = np.vstack((scan_angles, np.zeros(scan_len * 1,)))
+    inst = np.tile(inst[:, np.newaxis, :], [1, np.int(scan_nb), 1])
+
+    # building the corresponding times array
+    offset = np.arange(scan_nb) * scan_rate
+
+    times = (np.tile(scan_points * sampling_interval,
+                     [np.int(scan_nb), 1]) + np.expand_dims(offset, 1))
+
+    return ScanGeometry(inst, times)


=====================================
pyorbital/orbital.py
=====================================
@@ -28,10 +28,25 @@
 import warnings
 from datetime import datetime, timedelta
 
-import numpy as np
+from scipy import optimize
 
+import numpy as np
 from pyorbital import astronomy, dt2np, tlefile
 
+try:
+    import dask.array as da
+    has_dask = True
+except ImportError:
+    da = None
+    has_dask = False
+
+try:
+    import xarray as xr
+    has_xarray = True
+except ImportError:
+    xr = None
+    has_xarray = False
+
 ECC_EPS = 1.0e-6  # Too low for computing further drops.
 ECC_LIMIT_LOW = -1.0e-3
 ECC_LIMIT_HIGH = 1.0 - ECC_EPS  # Too close to 1
@@ -111,14 +126,22 @@ def get_observer_look(sat_lon, sat_lat, sat_alt, utc_time, lon, lat, alt):
 
     az_ = np.arctan(-top_e / top_s)
 
-    if hasattr(az_, 'chunks'):
-        # dask array
-        import dask.array as da
-        az_ = da.where(top_s > 0, az_ + np.pi, az_)
-        az_ = da.where(az_ < 0, az_ + 2 * np.pi, az_)
+    if has_xarray and isinstance(az_, xr.DataArray):
+        az_data = az_.data
     else:
-        az_[top_s > 0] += np.pi
-        az_[az_ < 0] += 2 * np.pi
+        az_data = az_
+
+    if has_dask and isinstance(az_data, da.Array):
+        az_data = da.where(top_s > 0, az_data + np.pi, az_data)
+        az_data = da.where(az_data < 0, az_data + 2 * np.pi, az_data)
+    else:
+        az_data[np.where(top_s > 0)] += np.pi
+        az_data[np.where(az_data < 0)] += 2 * np.pi
+
+    if has_xarray and isinstance(az_, xr.DataArray):
+        az_.data = az_data
+    else:
+        az_ = az_data
 
     rg_ = np.sqrt(rx * rx + ry * ry + rz * rz)
     el_ = np.arcsin(top_z / rg_)
@@ -131,7 +154,7 @@ class Orbital(object):
     """Class for orbital computations.
 
     The *satellite* parameter is the name of the satellite to work on and is
-    used to retreive the right TLE data for internet or from *tle_file* in case
+    used to retrieve the right TLE data for internet or from *tle_file* in case
     it is provided.
     """
 
@@ -337,8 +360,8 @@ class Orbital(object):
             """Compute the inverse of elevation."""
             return -elevation(minutes)
 
-        def get_root_secant(fun, start, end, tol=0.01):
-            """Secant method."""
+        def get_root(fun, start, end, tol=0.01):
+            """Root finding scheme"""
             x_0 = end
             x_1 = start
             fx_0 = fun(end)
@@ -346,11 +369,9 @@ class Orbital(object):
             if abs(fx_0) < abs(fx_1):
                 fx_0, fx_1 = fx_1, fx_0
                 x_0, x_1 = x_1, x_0
-            while abs(x_0 - x_1) > tol:
-                x_n = x_1 - fx_1 * ((x_1 - x_0) / (fx_1 - fx_0))
-                x_0, x_1 = x_1, x_n
-                fx_0, fx_1 = fx_1, fun(x_n)
-            return x_1
+
+            x_n = optimize.brentq(fun, x_0, x_1)
+            return x_n
 
         def get_max_parab(fun, start, end, tol=0.01):
             """Successive parabolic interpolation."""
@@ -385,7 +406,7 @@ class Orbital(object):
         risetime = None
         falltime = None
         for guess in zcs:
-            horizon_mins = get_root_secant(
+            horizon_mins = get_root(
                 elevation, guess, guess + 1.0, tol=tol / 60.0)
             horizon_time = utc_time + timedelta(minutes=horizon_mins)
             if elev[guess] < 0:


=====================================
pyorbital/tests/test_aiaa.py
=====================================
@@ -130,7 +130,7 @@ class AIAAIntegrationTest(unittest.TestCase):
 
                         delta_pos = 5e-6  # km =  5 mm
                         delta_vel = 5e-9  # km/s = 5 um/s
-                        delta_time = 1e-3  # 1 milisecond
+                        delta_time = 1e-3  # 1 millisecond
                         self.assertTrue(abs(res[0] - pos[0]) < delta_pos)
                         self.assertTrue(abs(res[1] - pos[1]) < delta_pos)
                         self.assertTrue(abs(res[2] - pos[2]) < delta_pos)


=====================================
pyorbital/tests/test_geoloc.py
=====================================
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2014, 2017 Martin Raspaud
+# Copyright (c) 2014, 2017, 2018 Martin Raspaud
 
 # Author(s):
 
@@ -29,7 +29,13 @@ from datetime import datetime, timedelta
 import numpy as np
 
 from pyorbital.geoloc import ScanGeometry, geodetic_lat, qrotate, subpoint
-from pyorbital.geoloc_instrument_definitions import avhrr, viirs, amsua, mhs, hirs4, atms
+from pyorbital.geoloc_instrument_definitions import (avhrr,
+                                                     viirs,
+                                                     amsua,
+                                                     mhs,
+                                                     hirs4,
+                                                     atms,
+                                                     ascat)
 
 
 class TestQuaternion(unittest.TestCase):
@@ -189,8 +195,17 @@ class TestGeolocDefs(unittest.TestCase):
         """
         geom = viirs(1, np.array([0, 3200, 6399]))
         expected_fovs = np.array([
-            np.tile(np.array([[ 0.98, -0.  , -0.98]]), [32,1]),
-            np.tile(np.array([[ 0.  , -0.  , 0    ]]), [32,1])], dtype=np.float)
+            np.tile(np.array([[0.98, -0., -0.98]]), [32, 1]),
+            np.tile(np.array([[0., -0., 0]]), [32, 1])], dtype=np.float)
+
+        self.assertTrue(np.allclose(geom.fovs,
+                                    expected_fovs, rtol=1e-2, atol=1e-2))
+
+        geom = viirs(2, np.array([0, 3200, 6399]))
+        expected_fovs = np.array([
+            np.tile(np.array([[0.98, -0., -0.98]]), [32*2, 1]),
+            np.tile(np.array([[0., -0., 0]]), [32*2, 1])], dtype=np.float)
+
         self.assertTrue(np.allclose(geom.fovs,
                                     expected_fovs, rtol=1e-2, atol=1e-2))
 
@@ -199,9 +214,9 @@ class TestGeolocDefs(unittest.TestCase):
         """
         geom = amsua(1)
         expected_fovs = np.array([
-            [[ 0.84,  0.78,  0.73,  0.67,  0.61,  0.55,  0.49,  0.44,  0.38,
-               0.32,  0.26,  0.2 ,  0.15,  0.09,  0.03, -0.03, -0.09, -0.15,
-              -0.2 , -0.26, -0.32, -0.38, -0.44, -0.49, -0.55, -0.61, -0.67,
+            [[0.84,  0.78,  0.73,  0.67,  0.61,  0.55,  0.49,  0.44,  0.38,
+              0.32,  0.26,  0.2,  0.15,  0.09,  0.03, -0.03, -0.09, -0.15,
+              -0.2, -0.26, -0.32, -0.38, -0.44, -0.49, -0.55, -0.61, -0.67,
               -0.73, -0.78, -0.84]],
             np.zeros((1, 30))], dtype=np.float)
         self.assertTrue(np.allclose(geom.fovs, expected_fovs, rtol=1e-2, atol=1e-2))
@@ -211,16 +226,16 @@ class TestGeolocDefs(unittest.TestCase):
         """
         geom = mhs(1)
         expected_fovs = np.array([
-            [[ 0.86,  0.84,  0.82,  0.8 ,  0.79,  0.77,  0.75,  0.73,  0.71,
-               0.69,  0.67,  0.65,  0.63,  0.61,  0.59,  0.57,  0.55,  0.53,
-               0.51,  0.49,  0.48,  0.46,  0.44,  0.42,  0.4 ,  0.38,  0.36,
-               0.34,  0.32,  0.3 ,  0.28,  0.26,  0.24,  0.22,  0.2 ,  0.18,
-               0.16,  0.15,  0.13,  0.11,  0.09,  0.07,  0.05,  0.03,  0.01,
+            [[0.86,  0.84,  0.82,  0.8,  0.79,  0.77,  0.75,  0.73,  0.71,
+              0.69,  0.67,  0.65,  0.63,  0.61,  0.59,  0.57,  0.55,  0.53,
+              0.51,  0.49,  0.48,  0.46,  0.44,  0.42,  0.4,  0.38,  0.36,
+              0.34,  0.32,  0.3,  0.28,  0.26,  0.24,  0.22,  0.2,  0.18,
+              0.16,  0.15,  0.13,  0.11,  0.09,  0.07,  0.05,  0.03,  0.01,
               -0.01, -0.03, -0.05, -0.07, -0.09, -0.11, -0.13, -0.15, -0.16,
-              -0.18, -0.2 , -0.22, -0.24, -0.26, -0.28, -0.3 , -0.32, -0.34,
-              -0.36, -0.38, -0.4 , -0.42, -0.44, -0.46, -0.48, -0.49, -0.51,
+              -0.18, -0.2, -0.22, -0.24, -0.26, -0.28, -0.3, -0.32, -0.34,
+              -0.36, -0.38, -0.4, -0.42, -0.44, -0.46, -0.48, -0.49, -0.51,
               -0.53, -0.55, -0.57, -0.59, -0.61, -0.63, -0.65, -0.67, -0.69,
-              -0.71, -0.73, -0.75, -0.77, -0.79, -0.8 , -0.82, -0.84, -0.86]],
+              -0.71, -0.73, -0.75, -0.77, -0.79, -0.8, -0.82, -0.84, -0.86]],
             np.zeros((1, 90))], dtype=np.float)
         self.assertTrue(np.allclose(geom.fovs,
                                     expected_fovs, rtol=1e-2, atol=1e-2))
@@ -230,12 +245,12 @@ class TestGeolocDefs(unittest.TestCase):
         """
         geom = hirs4(1)
         expected_fovs = np.array([
-            [[ 0.86,  0.83,  0.8 ,  0.77,  0.74,  0.71,  0.68,  0.64,  0.61,
-               0.58,  0.55,  0.52,  0.49,  0.46,  0.42,  0.39,  0.36,  0.33,
-               0.3 ,  0.27,  0.24,  0.2 ,  0.17,  0.14,  0.11,  0.08,  0.05,
-               0.02, -0.02, -0.05, -0.08, -0.11, -0.14, -0.17, -0.2 , -0.24,
-              -0.27, -0.3 , -0.33, -0.36, -0.39, -0.42, -0.46, -0.49, -0.52,
-              -0.55, -0.58, -0.61, -0.64, -0.68, -0.71, -0.74, -0.77, -0.8 ,
+            [[0.86,  0.83,  0.8,  0.77,  0.74,  0.71,  0.68,  0.64,  0.61,
+              0.58,  0.55,  0.52,  0.49,  0.46,  0.42,  0.39,  0.36,  0.33,
+              0.3,  0.27,  0.24,  0.2,  0.17,  0.14,  0.11,  0.08,  0.05,
+              0.02, -0.02, -0.05, -0.08, -0.11, -0.14, -0.17, -0.2, -0.24,
+              -0.27, -0.3, -0.33, -0.36, -0.39, -0.42, -0.46, -0.49, -0.52,
+              -0.55, -0.58, -0.61, -0.64, -0.68, -0.71, -0.74, -0.77, -0.8,
               -0.83, -0.86]],
             np.zeros((1, 56))], dtype=np.float)
         self.assertTrue(np.allclose(geom.fovs,
@@ -246,21 +261,50 @@ class TestGeolocDefs(unittest.TestCase):
         """
         geom = atms(1)
         expected_fovs = np.array([
-            [[ 0.92,  0.9 ,  0.88,  0.86,  0.84,  0.82,  0.8 ,  0.78,  0.76,
-               0.75,  0.73,  0.71,  0.69,  0.67,  0.65,  0.63,  0.61,  0.59,
-               0.57,  0.55,  0.53,  0.51,  0.49,  0.47,  0.46,  0.44,  0.42,
-               0.4 ,  0.38,  0.36,  0.34,  0.32,  0.3 ,  0.28,  0.26,  0.24,
-               0.22,  0.2 ,  0.18,  0.16,  0.15,  0.13,  0.11,  0.09,  0.07,
-               0.05,  0.03,  0.01, -0.01, -0.03, -0.05, -0.07, -0.09, -0.11,
-              -0.13, -0.15, -0.16, -0.18, -0.2 , -0.22, -0.24, -0.26, -0.28,
-              -0.3 , -0.32, -0.34, -0.36, -0.38, -0.4 , -0.42, -0.44, -0.46,
+            [[0.92,  0.9,  0.88,  0.86,  0.84,  0.82,  0.8,  0.78,  0.76,
+              0.75,  0.73,  0.71,  0.69,  0.67,  0.65,  0.63,  0.61,  0.59,
+              0.57,  0.55,  0.53,  0.51,  0.49,  0.47,  0.46,  0.44,  0.42,
+              0.4,  0.38,  0.36,  0.34,  0.32,  0.3,  0.28,  0.26,  0.24,
+              0.22,  0.2,  0.18,  0.16,  0.15,  0.13,  0.11,  0.09,  0.07,
+              0.05,  0.03,  0.01, -0.01, -0.03, -0.05, -0.07, -0.09, -0.11,
+              -0.13, -0.15, -0.16, -0.18, -0.2, -0.22, -0.24, -0.26, -0.28,
+              -0.3, -0.32, -0.34, -0.36, -0.38, -0.4, -0.42, -0.44, -0.46,
               -0.47, -0.49, -0.51, -0.53, -0.55, -0.57, -0.59, -0.61, -0.63,
-              -0.65, -0.67, -0.69, -0.71, -0.73, -0.75, -0.76, -0.78, -0.8 ,
-              -0.82, -0.84, -0.86, -0.88, -0.9 , -0.92]],
+              -0.65, -0.67, -0.69, -0.71, -0.73, -0.75, -0.76, -0.78, -0.8,
+              -0.82, -0.84, -0.86, -0.88, -0.9, -0.92]],
             np.zeros((1, 96))], dtype=np.float)
         self.assertTrue(np.allclose(geom.fovs,
                                     expected_fovs, rtol=1e-2, atol=1e-2))
 
+    def test_ascat(self):
+        """Test the definition of the ASCAT instrument onboard Metop"""
+
+        geom = ascat(1)
+        expected_fovs = np.array([
+            [[0.9250245,  0.90058989,  0.87615528,  0.85172067,
+              0.82728607,  0.80285146,  0.77841685,  0.75398224,
+              0.72954763,  0.70511302,  0.68067841,  0.6562438,
+              0.63180919,  0.60737458,  0.58293997,  0.55850536,
+              0.53407075,  0.50963614,  0.48520153,  0.46076692,
+              0.43633231, -0.43633231, -0.46076692, -0.48520153,
+              -0.50963614, -0.53407075, -0.55850536, -0.58293997,
+              -0.60737458, -0.63180919, -0.6562438, -0.68067841,
+              -0.70511302, -0.72954763, -0.75398224, -0.77841685,
+              -0.80285146, -0.82728607, -0.85172067, -0.87615528,
+              -0.90058989, -0.9250245]], np.zeros((1, 42))], dtype=np.float)
+
+        self.assertTrue(np.allclose(
+            geom.fovs, expected_fovs, rtol=1e-2, atol=1e-2))
+        geom = ascat(1, np.array([0, 41]))
+        expected_fovs = np.array([[[0.9250245,  -0.9250245]],
+                                  [[0.,  0.]]], dtype=np.float)
+        self.assertTrue(np.allclose(
+            geom.fovs, expected_fovs, rtol=1e-2, atol=1e-2))
+
+        geom = ascat(1, np.array([0, -1]))
+        self.assertTrue(np.allclose(
+            geom.fovs, expected_fovs, rtol=1e-2, atol=1e-2))
+
 
 def suite():
     """The suite for test_geoloc


=====================================
pyorbital/tests/test_orbital.py
=====================================
@@ -38,54 +38,71 @@ class Test(unittest.TestCase):
     def test_get_orbit_number(self):
         """Testing getting the orbitnumber from the tle"""
         sat = orbital.Orbital("NPP",
-                              line1="1 37849U 11061A   12017.90990040 -.00000112  00000-0 -32693-4 0   772",
-                              line2="2 37849  98.7026 317.8811 0001845  92.4533 267.6830 14.19582686 11574")
+                              line1="1 37849U 11061A   12017.90990040 "
+                                    "-.00000112  00000-0 -32693-4 0   772",
+                              line2="2 37849  98.7026 317.8811 0001845  "
+                                    "92.4533 267.6830 14.19582686 11574")
         dobj = datetime(2012, 1, 18, 8, 4, 19)
         orbnum = sat.get_orbit_number(dobj)
         self.assertEqual(orbnum, 1163)
 
     def test_sublonlat(self):
         sat = orbital.Orbital("ISS (ZARYA)",
-                              line1="1 25544U 98067A   03097.78853147  .00021906  00000-0  28403-3 0  8652",
-                              line2="2 25544  51.6361  13.7980 0004256  35.6671  59.2566 15.58778559250029")
+                              line1="1 25544U 98067A   03097.78853147  "
+                                    ".00021906  00000-0  28403-3 0  8652",
+                              line2="2 25544  51.6361  13.7980 0004256  "
+                                    "35.6671  59.2566 15.58778559250029")
         d = datetime(2003, 3, 23, 0, 3, 22)
         lon, lat, alt = sat.get_lonlatalt(d)
         expected_lon = -68.199894472013213
         expected_lat = 23.159747677881075
         expected_alt = 392.01953430856935
-        self.assertTrue(np.abs(lon - expected_lon) < eps_deg, 'Calculation of sublon failed')
-        self.assertTrue(np.abs(lat - expected_lat) < eps_deg, 'Calculation of sublat failed')
-        self.assertTrue(np.abs(alt - expected_alt) < eps_deg, 'Calculation of altitude failed')
+        self.assertTrue(np.abs(lon - expected_lon) < eps_deg,
+                        'Calculation of sublon failed')
+        self.assertTrue(np.abs(lat - expected_lat) < eps_deg,
+                        'Calculation of sublat failed')
+        self.assertTrue(np.abs(alt - expected_alt) < eps_deg,
+                        'Calculation of altitude failed')
 
     def test_observer_look(self):
         sat = orbital.Orbital("ISS (ZARYA)",
-                              line1="1 25544U 98067A   03097.78853147  .00021906  00000-0  28403-3 0  8652",
-                              line2="2 25544  51.6361  13.7980 0004256  35.6671  59.2566 15.58778559250029")
+                              line1="1 25544U 98067A   03097.78853147  "
+                                    ".00021906  00000-0  28403-3 0  8652",
+                              line2="2 25544  51.6361  13.7980 0004256  "
+                                    "35.6671  59.2566 15.58778559250029")
         d = datetime(2003, 3, 23, 0, 3, 22)
         az, el = sat.get_observer_look(d, -84.39733, 33.775867, 0)
         expected_az = 122.45169655331965
         expected_el = 1.9800219611255456
-        self.assertTrue(np.abs(az - expected_az) < eps_deg, 'Calculation of azimut failed')
-        self.assertTrue(np.abs(el - expected_el) < eps_deg, 'Calculation of elevation failed')
+        self.assertTrue(np.abs(az - expected_az) < eps_deg,
+                        'Calculation of azimut failed')
+        self.assertTrue(np.abs(el - expected_el) < eps_deg,
+                        'Calculation of elevation failed')
 
     def test_orbit_num_an(self):
         sat = orbital.Orbital("METOP-A",
-                              line1="1 29499U 06044A   11254.96536486  .00000092  00000-0  62081-4 0  5221",
-                              line2="2 29499  98.6804 312.6735 0001758 111.9178 248.2152 14.21501774254058")
+                              line1="1 29499U 06044A   11254.96536486  "
+                                    ".00000092  00000-0  62081-4 0  5221",
+                              line2="2 29499  98.6804 312.6735 0001758 "
+                                    "111.9178 248.2152 14.21501774254058")
         d = datetime(2011, 9, 14, 5, 30)
         self.assertEqual(sat.get_orbit_number(d), 25437)
 
     def test_orbit_num_non_an(self):
         sat = orbital.Orbital("METOP-A",
-                              line1="1 29499U 06044A   13060.48822809  .00000017  00000-0  27793-4 0  9819",
-                              line2="2 29499  98.6639 121.6164 0001449  71.9056  43.3132 14.21510544330271")
+                              line1="1 29499U 06044A   13060.48822809  "
+                                    ".00000017  00000-0  27793-4 0  9819",
+                              line2="2 29499  98.6639 121.6164 0001449  "
+                                    "71.9056  43.3132 14.21510544330271")
         dt = np.timedelta64(98, 'm')
         self.assertEqual(sat.get_orbit_number(sat.tle.epoch + dt), 33028)
 
     def test_orbit_num_equator(self):
         sat = orbital.Orbital("SUOMI NPP",
-                              line1="1 37849U 11061A   13061.24611272  .00000048  00000-0  43679-4 0  4334",
-                              line2="2 37849  98.7444   1.0588 0001264  63.8791 102.8546 14.19528338 69643")
+                              line1="1 37849U 11061A   13061.24611272  "
+                                    ".00000048  00000-0  43679-4 0  4334",
+                              line2="2 37849  98.7444   1.0588 0001264  "
+                                    "63.8791 102.8546 14.19528338 69643")
         t1 = datetime(2013, 3, 2, 22, 2, 25)
         t2 = datetime(2013, 3, 2, 22, 2, 26)
         on1 = sat.get_orbit_number(t1)
@@ -100,14 +117,120 @@ class Test(unittest.TestCase):
 
     def test_get_next_passes_apogee(self):
         """Regression test #22."""
-        line1 = "1 24793U 97020B   18065.48735489  .00000075  00000-0  19863-4 0  9994"
-        line2 = "2 24793  86.3994 209.3241 0002020  89.8714 270.2713 14.34246429 90794"
+        line1 = "1 24793U 97020B   18065.48735489  " \
+                ".00000075  00000-0  19863-4 0  9994"
+        line2 = "2 24793  86.3994 209.3241 0002020  " \
+                "89.8714 270.2713 14.34246429 90794"
 
         orb = orbital.Orbital('IRIDIUM 7 [+]', line1=line1, line2=line2)
         d = datetime(2018, 3, 7, 3, 30, 15)
         res = orb.get_next_passes(d, 1, 170.556, -43.368, 0.5, horizon=40)
-        self.assertTrue(abs(res[0][2] - datetime(2018, 3, 7, 3, 48, 13, 178439)) < timedelta(seconds=0.01))
-
+        self.assertTrue(abs(
+            res[0][2] - datetime(2018, 3, 7, 3, 48, 13, 178439)) <
+            timedelta(seconds=0.01))
+
+    def test_get_next_passes_tricky(self):
+        """ Check issue #34 for reference """
+        line1 = "1 43125U 18004Q   18251.42128650 " \
+            "+.00001666 +00000-0 +73564-4 0  9991"
+
+        line2 = "2 43125 097.5269 314.3317 0010735 "\
+            "157.6344 202.5362 15.23132245036381"
+
+        orb = orbital.Orbital('LEMUR-2-BROWNCOW', line1=line1, line2=line2)
+        d = datetime(2018, 9, 8)
+
+        res = orb.get_next_passes(d, 72, -8.174163, 51.953319, 0.05, horizon=5)
+
+        self.assertTrue(abs(
+            res[0][2] - datetime(2018, 9, 8, 9, 5, 46, 375248)) <
+            timedelta(seconds=0.01))
+        self.assertTrue(abs(
+            res[-1][2] - datetime(2018, 9, 10, 22, 15, 3, 143469)) <
+            timedelta(seconds=0.01))
+
+        self.assertTrue(len(res) == 15)
+
+
+class TestGetObserverLook(unittest.TestCase):
+    """Test the get_observer_look function"""
+
+    def setUp(self):
+        self.t = datetime(2018, 1, 1, 0, 0, 0)
+        self.sat_lon = np.array([[-89.5, -89.4], [-89.3, -89.2]])
+        self.sat_lat = np.array([[45.5, 45.4], [45.3, 45.2]])
+        self.sat_alt = np.array([[35786, 35786], [35786, 35786]])
+        self.lon = np.array([[-85.5, -85.4], [-85.3, -85.2]])
+        self.lat = np.array([[40.5, 40.4], [40.3, 40.2]])
+        self.alt = np.zeros((2, 2))
+        self.exp_azi = np.array([[331.00275902, 330.95954165],
+                                 [330.91642994, 330.87342384]])
+        self.exp_elev = np.array([[83.18070976, 83.17788976],
+                                  [83.17507167, 83.1722555]])
+
+    def test_basic_numpy(self):
+        """Test with numpy array inputs"""
+        from pyorbital import orbital
+        azi, elev = orbital.get_observer_look(self.sat_lon, self.sat_lat,
+                                              self.sat_alt, self.t,
+                                              self.lon, self.lat, self.alt)
+        np.testing.assert_allclose(azi, self.exp_azi)
+        np.testing.assert_allclose(elev, self.exp_elev)
+
+    def test_basic_dask(self):
+        """Test with dask array inputs"""
+        from pyorbital import orbital
+        import dask.array as da
+        sat_lon = da.from_array(self.sat_lon, chunks=2)
+        sat_lat = da.from_array(self.sat_lat, chunks=2)
+        sat_alt = da.from_array(self.sat_alt, chunks=2)
+        lon = da.from_array(self.lon, chunks=2)
+        lat = da.from_array(self.lat, chunks=2)
+        alt = da.from_array(self.alt, chunks=2)
+        azi, elev = orbital.get_observer_look(sat_lon, sat_lat,
+                                              sat_alt, self.t,
+                                              lon, lat, alt)
+        np.testing.assert_allclose(azi.compute(), self.exp_azi)
+        np.testing.assert_allclose(elev.compute(), self.exp_elev)
+
+    def test_xarray_with_numpy(self):
+        """Test with xarray DataArray with numpy array as inputs"""
+        from pyorbital import orbital
+        import xarray as xr
+
+        def _xarr_conv(input):
+            return xr.DataArray(input)
+        sat_lon = _xarr_conv(self.sat_lon)
+        sat_lat = _xarr_conv(self.sat_lat)
+        sat_alt = _xarr_conv(self.sat_alt)
+        lon = _xarr_conv(self.lon)
+        lat = _xarr_conv(self.lat)
+        alt = _xarr_conv(self.alt)
+        azi, elev = orbital.get_observer_look(sat_lon, sat_lat,
+                                              sat_alt, self.t,
+                                              lon, lat, alt)
+        np.testing.assert_allclose(azi.data, self.exp_azi)
+        np.testing.assert_allclose(elev.data, self.exp_elev)
+
+    def test_xarray_with_dask(self):
+        """Test with xarray DataArray with dask array as inputs"""
+        from pyorbital import orbital
+        import dask.array as da
+        import xarray as xr
+
+        def _xarr_conv(input):
+            return xr.DataArray(da.from_array(input, chunks=2))
+        sat_lon = _xarr_conv(self.sat_lon)
+        sat_lat = _xarr_conv(self.sat_lat)
+        sat_alt = _xarr_conv(self.sat_alt)
+        lon = _xarr_conv(self.lon)
+        lat = _xarr_conv(self.lat)
+        alt = _xarr_conv(self.alt)
+        azi, elev = orbital.get_observer_look(sat_lon, sat_lat,
+                                              sat_alt, self.t,
+                                              lon, lat, alt)
+        np.testing.assert_allclose(azi.data.compute(), self.exp_azi)
+        np.testing.assert_allclose(elev.data.compute(), self.exp_elev)
 
 
 def suite():
@@ -116,5 +239,6 @@ def suite():
     loader = unittest.TestLoader()
     mysuite = unittest.TestSuite()
     mysuite.addTest(loader.loadTestsFromTestCase(Test))
+    mysuite.addTest(loader.loadTestsFromTestCase(TestGetObserverLook))
 
     return mysuite


=====================================
pyorbital/tlefile.py
=====================================
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2011, 2012, 2013, 2014, 2015.
+# Copyright (c) 2011 - 2018
 
 # Author(s):
 
@@ -78,63 +78,9 @@ SATELLITES = read_platform_numbers(in_upper=True, num_as_int=False)
 The platform numbers are given in a file $PPP_CONFIG/platforms.txt
 in the following format:
 
-# Mappings between satellite catalogue numbers and corresponding
-# platform names from OSCAR.
-ALOS-2 39766
-CloudSat 29107
-CryoSat-2 36508
-CSK-1 31598
-CSK-2 32376
-CSK-3 33412
-CSK-4 37216
-DMSP-F15 25991
-DMSP-F16 28054
-DMSP-F17 29522
-DMSP-F18 35951
-DMSP-F19 39630
-EOS-Aqua 27424
-EOS-Aura 28376
-EOS-Terra 25994
-FY-2D 29640
-FY-2E 33463
-FY-2F 38049
-FY-2G 40367
-FY-3A 32958
-FY-3B 37214
-FY-3C 39260
-GOES-13 29155
-GOES-14 35491
-GOES-15 36411
-Himawari-6 28622
-Himawari-7 28937
-Himawari-8 40267
-INSAT-3A 27714
-INSAT-3C 27298
-INSAT-3D 39216
-JASON-2 33105
-Kalpana-1 27525
-Landsat-7 25682
-Landsat-8 39084
-Meteosat-7 24932
-Meteosat-8 27509
-Meteosat-9 28912
-Meteosat-10 38552
-Metop-A 29499
-Metop-B 38771
-NOAA-15 25338
-NOAA-16 26536
-NOAA-17 27453
-NOAA-18 28654
-NOAA-19 33591
-RadarSat-2 32382
-Sentinel-1A 39634
-SMOS 36036
-SPOT-5 27421
-SPOT-6 38755
-SPOT-7 40053
-Suomi-NPP 37849
-TanDEM-X 36605
-TerraSAR-X 31698
+.. literalinclude:: ../../etc/platforms.txt
+  :language: text
+  :lines: 4-
 '''
 
 
@@ -343,5 +289,6 @@ def main():
     tle_data = read('Noaa-19')
     print(tle_data)
 
+
 if __name__ == '__main__':
     main()


=====================================
pyorbital/version.py
=====================================
@@ -1,26 +1,520 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
 
-# Copyright (c) 2014, 2015 Martin Raspaud
+# This file helps to compute a version number in source trees obtained from
+# git-archive tarball (such as those provided by githubs download-from-tag
+# feature). Distribution tarballs (built by setup.py sdist) and build
+# directories (produced by setup.py build) will contain a much shorter file
+# that just contains the computed version number.
 
-# Author(s):
+# This file is released into the public domain. Generated by
+# versioneer-0.18 (https://github.com/warner/python-versioneer)
 
-#   Martin Raspaud <martin.raspaud at smhi.se>
+"""Git implementation of _version.py."""
 
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+import errno
+import os
+import re
+import subprocess
+import sys
 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
 
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+def get_keywords():
+    """Get the keywords needed to look up the version information."""
+    # these strings will be replaced by git during git-archive.
+    # setup.py/versioneer.py will grep for the variable names, so they must
+    # each be defined on a line of their own. _version.py will just call
+    # get_keywords().
+    git_refnames = " (HEAD -> master, tag: v1.4.0)"
+    git_full = "eb3dadc23abc7e7a192078cd0036622a120e4d49"
+    git_date = "2018-10-23 11:29:26 +0200"
+    keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
+    return keywords
 
-"""Version file.
-"""
 
-__version__ = "v1.3.1"
+class VersioneerConfig:
+    """Container for Versioneer configuration parameters."""
+
+
+def get_config():
+    """Create, populate and return the VersioneerConfig() object."""
+    # these strings are filled in when 'setup.py versioneer' creates
+    # _version.py
+    cfg = VersioneerConfig()
+    cfg.VCS = "git"
+    cfg.style = "pep440"
+    cfg.tag_prefix = "v"
+    cfg.parentdir_prefix = "None"
+    cfg.versionfile_source = "pyorbital/version.py"
+    cfg.verbose = False
+    return cfg
+
+
+class NotThisMethod(Exception):
+    """Exception raised if a method is not valid for the current scenario."""
+
+
+LONG_VERSION_PY = {}
+HANDLERS = {}
+
+
+def register_vcs_handler(vcs, method):  # decorator
+    """Decorator to mark a method as the handler for a particular VCS."""
+    def decorate(f):
+        """Store f in HANDLERS[vcs][method]."""
+        if vcs not in HANDLERS:
+            HANDLERS[vcs] = {}
+        HANDLERS[vcs][method] = f
+        return f
+    return decorate
+
+
+def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
+                env=None):
+    """Call the given command(s)."""
+    assert isinstance(commands, list)
+    p = None
+    for c in commands:
+        try:
+            dispcmd = str([c] + args)
+            # remember shell=False, so use git.cmd on windows, not just git
+            p = subprocess.Popen([c] + args, cwd=cwd, env=env,
+                                 stdout=subprocess.PIPE,
+                                 stderr=(subprocess.PIPE if hide_stderr
+                                         else None))
+            break
+        except EnvironmentError:
+            e = sys.exc_info()[1]
+            if e.errno == errno.ENOENT:
+                continue
+            if verbose:
+                print("unable to run %s" % dispcmd)
+                print(e)
+            return None, None
+    else:
+        if verbose:
+            print("unable to find command, tried %s" % (commands,))
+        return None, None
+    stdout = p.communicate()[0].strip()
+    if sys.version_info[0] >= 3:
+        stdout = stdout.decode()
+    if p.returncode != 0:
+        if verbose:
+            print("unable to run %s (error)" % dispcmd)
+            print("stdout was %s" % stdout)
+        return None, p.returncode
+    return stdout, p.returncode
+
+
+def versions_from_parentdir(parentdir_prefix, root, verbose):
+    """Try to determine the version from the parent directory name.
+
+    Source tarballs conventionally unpack into a directory that includes both
+    the project name and a version string. We will also support searching up
+    two directory levels for an appropriately named parent directory
+    """
+    rootdirs = []
+
+    for i in range(3):
+        dirname = os.path.basename(root)
+        if dirname.startswith(parentdir_prefix):
+            return {"version": dirname[len(parentdir_prefix):],
+                    "full-revisionid": None,
+                    "dirty": False, "error": None, "date": None}
+        else:
+            rootdirs.append(root)
+            root = os.path.dirname(root)  # up a level
+
+    if verbose:
+        print("Tried directories %s but none started with prefix %s" %
+              (str(rootdirs), parentdir_prefix))
+    raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
+
+
+ at register_vcs_handler("git", "get_keywords")
+def git_get_keywords(versionfile_abs):
+    """Extract version information from the given file."""
+    # the code embedded in _version.py can just fetch the value of these
+    # keywords. When used from setup.py, we don't want to import _version.py,
+    # so we do it with a regexp instead. This function is not used from
+    # _version.py.
+    keywords = {}
+    try:
+        f = open(versionfile_abs, "r")
+        for line in f.readlines():
+            if line.strip().startswith("git_refnames ="):
+                mo = re.search(r'=\s*"(.*)"', line)
+                if mo:
+                    keywords["refnames"] = mo.group(1)
+            if line.strip().startswith("git_full ="):
+                mo = re.search(r'=\s*"(.*)"', line)
+                if mo:
+                    keywords["full"] = mo.group(1)
+            if line.strip().startswith("git_date ="):
+                mo = re.search(r'=\s*"(.*)"', line)
+                if mo:
+                    keywords["date"] = mo.group(1)
+        f.close()
+    except EnvironmentError:
+        pass
+    return keywords
+
+
+ at register_vcs_handler("git", "keywords")
+def git_versions_from_keywords(keywords, tag_prefix, verbose):
+    """Get version information from git keywords."""
+    if not keywords:
+        raise NotThisMethod("no keywords at all, weird")
+    date = keywords.get("date")
+    if date is not None:
+        # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
+        # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
+        # -like" string, which we must then edit to make compliant), because
+        # it's been around since git-1.5.3, and it's too difficult to
+        # discover which version we're using, or to work around using an
+        # older one.
+        date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
+    refnames = keywords["refnames"].strip()
+    if refnames.startswith("$Format"):
+        if verbose:
+            print("keywords are unexpanded, not using")
+        raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
+    refs = set([r.strip() for r in refnames.strip("()").split(",")])
+    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
+    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
+    TAG = "tag: "
+    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
+    if not tags:
+        # Either we're using git < 1.8.3, or there really are no tags. We use
+        # a heuristic: assume all version tags have a digit. The old git %d
+        # expansion behaves like git log --decorate=short and strips out the
+        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
+        # between branches and tags. By ignoring refnames without digits, we
+        # filter out many common branch names like "release" and
+        # "stabilization", as well as "HEAD" and "master".
+        tags = set([r for r in refs if re.search(r'\d', r)])
+        if verbose:
+            print("discarding '%s', no digits" % ",".join(refs - tags))
+    if verbose:
+        print("likely tags: %s" % ",".join(sorted(tags)))
+    for ref in sorted(tags):
+        # sorting will prefer e.g. "2.0" over "2.0rc1"
+        if ref.startswith(tag_prefix):
+            r = ref[len(tag_prefix):]
+            if verbose:
+                print("picking %s" % r)
+            return {"version": r,
+                    "full-revisionid": keywords["full"].strip(),
+                    "dirty": False, "error": None,
+                    "date": date}
+    # no suitable tags, so version is "0+unknown", but full hex is still there
+    if verbose:
+        print("no suitable tags, using unknown + full revision id")
+    return {"version": "0+unknown",
+            "full-revisionid": keywords["full"].strip(),
+            "dirty": False, "error": "no suitable tags", "date": None}
+
+
+ at register_vcs_handler("git", "pieces_from_vcs")
+def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
+    """Get version from 'git describe' in the root of the source tree.
+
+    This only gets called if the git-archive 'subst' keywords were *not*
+    expanded, and _version.py hasn't already been rewritten with a short
+    version string, meaning we're inside a checked out source tree.
+    """
+    GITS = ["git"]
+    if sys.platform == "win32":
+        GITS = ["git.cmd", "git.exe"]
+
+    out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
+                          hide_stderr=True)
+    if rc != 0:
+        if verbose:
+            print("Directory %s not under git control" % root)
+        raise NotThisMethod("'git rev-parse --git-dir' returned error")
+
+    # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
+    # if there isn't one, this yields HEX[-dirty] (no NUM)
+    describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
+                                          "--always", "--long",
+                                          "--match", "%s*" % tag_prefix],
+                                   cwd=root)
+    # --long was added in git-1.5.5
+    if describe_out is None:
+        raise NotThisMethod("'git describe' failed")
+    describe_out = describe_out.strip()
+    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
+    if full_out is None:
+        raise NotThisMethod("'git rev-parse' failed")
+    full_out = full_out.strip()
+
+    pieces = {}
+    pieces["long"] = full_out
+    pieces["short"] = full_out[:7]  # maybe improved later
+    pieces["error"] = None
+
+    # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
+    # TAG might have hyphens.
+    git_describe = describe_out
+
+    # look for -dirty suffix
+    dirty = git_describe.endswith("-dirty")
+    pieces["dirty"] = dirty
+    if dirty:
+        git_describe = git_describe[:git_describe.rindex("-dirty")]
+
+    # now we have TAG-NUM-gHEX or HEX
+
+    if "-" in git_describe:
+        # TAG-NUM-gHEX
+        mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
+        if not mo:
+            # unparseable. Maybe git-describe is misbehaving?
+            pieces["error"] = ("unable to parse git-describe output: '%s'"
+                               % describe_out)
+            return pieces
+
+        # tag
+        full_tag = mo.group(1)
+        if not full_tag.startswith(tag_prefix):
+            if verbose:
+                fmt = "tag '%s' doesn't start with prefix '%s'"
+                print(fmt % (full_tag, tag_prefix))
+            pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
+                               % (full_tag, tag_prefix))
+            return pieces
+        pieces["closest-tag"] = full_tag[len(tag_prefix):]
+
+        # distance: number of commits since tag
+        pieces["distance"] = int(mo.group(2))
+
+        # commit: short hex revision ID
+        pieces["short"] = mo.group(3)
+
+    else:
+        # HEX: no tags
+        pieces["closest-tag"] = None
+        count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
+                                    cwd=root)
+        pieces["distance"] = int(count_out)  # total number of commits
+
+    # commit date: see ISO-8601 comment in git_versions_from_keywords()
+    date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"],
+                       cwd=root)[0].strip()
+    pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
+
+    return pieces
+
+
+def plus_or_dot(pieces):
+    """Return a + if we don't already have one, else return a ."""
+    if "+" in pieces.get("closest-tag", ""):
+        return "."
+    return "+"
+
+
+def render_pep440(pieces):
+    """Build up version string, with post-release "local version identifier".
+
+    Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
+    get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
+
+    Exceptions:
+    1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += plus_or_dot(pieces)
+            rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0+untagged.%d.g%s" % (pieces["distance"],
+                                          pieces["short"])
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
+def render_pep440_pre(pieces):
+    """TAG[.post.devDISTANCE] -- No -dirty.
+
+    Exceptions:
+    1: no tags. 0.post.devDISTANCE
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"]:
+            rendered += ".post.dev%d" % pieces["distance"]
+    else:
+        # exception #1
+        rendered = "0.post.dev%d" % pieces["distance"]
+    return rendered
+
+
+def render_pep440_post(pieces):
+    """TAG[.postDISTANCE[.dev0]+gHEX] .
+
+    The ".dev0" means dirty. Note that .dev0 sorts backwards
+    (a dirty tree will appear "older" than the corresponding clean one),
+    but you shouldn't be releasing software with -dirty anyways.
+
+    Exceptions:
+    1: no tags. 0.postDISTANCE[.dev0]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%d" % pieces["distance"]
+            if pieces["dirty"]:
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "g%s" % pieces["short"]
+    else:
+        # exception #1
+        rendered = "0.post%d" % pieces["distance"]
+        if pieces["dirty"]:
+            rendered += ".dev0"
+        rendered += "+g%s" % pieces["short"]
+    return rendered
+
+
+def render_pep440_old(pieces):
+    """TAG[.postDISTANCE[.dev0]] .
+
+    The ".dev0" means dirty.
+
+    Eexceptions:
+    1: no tags. 0.postDISTANCE[.dev0]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%d" % pieces["distance"]
+            if pieces["dirty"]:
+                rendered += ".dev0"
+    else:
+        # exception #1
+        rendered = "0.post%d" % pieces["distance"]
+        if pieces["dirty"]:
+            rendered += ".dev0"
+    return rendered
+
+
+def render_git_describe(pieces):
+    """TAG[-DISTANCE-gHEX][-dirty].
+
+    Like 'git describe --tags --dirty --always'.
+
+    Exceptions:
+    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"]:
+            rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
+    else:
+        # exception #1
+        rendered = pieces["short"]
+    if pieces["dirty"]:
+        rendered += "-dirty"
+    return rendered
+
+
+def render_git_describe_long(pieces):
+    """TAG-DISTANCE-gHEX[-dirty].
+
+    Like 'git describe --tags --dirty --always -long'.
+    The distance/hash is unconditional.
+
+    Exceptions:
+    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
+    else:
+        # exception #1
+        rendered = pieces["short"]
+    if pieces["dirty"]:
+        rendered += "-dirty"
+    return rendered
+
+
+def render(pieces, style):
+    """Render the given version pieces into the requested style."""
+    if pieces["error"]:
+        return {"version": "unknown",
+                "full-revisionid": pieces.get("long"),
+                "dirty": None,
+                "error": pieces["error"],
+                "date": None}
+
+    if not style or style == "default":
+        style = "pep440"  # the default
+
+    if style == "pep440":
+        rendered = render_pep440(pieces)
+    elif style == "pep440-pre":
+        rendered = render_pep440_pre(pieces)
+    elif style == "pep440-post":
+        rendered = render_pep440_post(pieces)
+    elif style == "pep440-old":
+        rendered = render_pep440_old(pieces)
+    elif style == "git-describe":
+        rendered = render_git_describe(pieces)
+    elif style == "git-describe-long":
+        rendered = render_git_describe_long(pieces)
+    else:
+        raise ValueError("unknown style '%s'" % style)
+
+    return {"version": rendered, "full-revisionid": pieces["long"],
+            "dirty": pieces["dirty"], "error": None,
+            "date": pieces.get("date")}
+
+
+def get_versions():
+    """Get version information or return default if unable to do so."""
+    # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
+    # __file__, we can work backwards from there to the root. Some
+    # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
+    # case we can only use expanded keywords.
+
+    cfg = get_config()
+    verbose = cfg.verbose
+
+    try:
+        return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
+                                          verbose)
+    except NotThisMethod:
+        pass
+
+    try:
+        root = os.path.realpath(__file__)
+        # versionfile_source is the relative path from the top of the source
+        # tree (where the .git directory might live) to this file. Invert
+        # this to find the root from __file__.
+        for i in cfg.versionfile_source.split('/'):
+            root = os.path.dirname(root)
+    except NameError:
+        return {"version": "0+unknown", "full-revisionid": None,
+                "dirty": None,
+                "error": "unable to find root of source tree",
+                "date": None}
+
+    try:
+        pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
+        return render(pieces, cfg.style)
+    except NotThisMethod:
+        pass
+
+    try:
+        if cfg.parentdir_prefix:
+            return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
+    except NotThisMethod:
+        pass
+
+    return {"version": "0+unknown", "full-revisionid": None,
+            "dirty": None,
+            "error": "unable to compute version", "date": None}


=====================================
setup.cfg
=====================================
@@ -3,3 +3,20 @@ requires=numpy
 release=1
 doc_files = doc/Makefile doc/source/*.rst
 
+[bdist_wheel]
+universal=1
+
+[flake8]
+max-line-length = 120
+
+[versioneer]
+VCS = git
+style = pep440
+versionfile_source = pyorbital/version.py
+versionfile_build =
+tag_prefix = v
+
+[coverage:run]
+omit =
+    pyorbital/version.py
+    versioneer.py


=====================================
setup.py
=====================================
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2011-2014
+# Copyright (c) 2011-2014, 2018
 
 # Author(s):
 
@@ -22,11 +22,11 @@
 
 from setuptools import setup
 import imp
-
-version = imp.load_source('pyorbital.version', 'pyorbital/version.py')
+import versioneer
 
 setup(name='pyorbital',
-      version=version.__version__,
+      version=versioneer.get_version(),
+      cmdclass=versioneer.get_cmdclass(),
       description='Orbital parameters and astronomical computations in Python',
       author='Martin Raspaud, Esben S. Nielsen',
       author_email='martin.raspaud at smhi.se',
@@ -38,10 +38,10 @@ setup(name='pyorbital',
                    "Programming Language :: Python",
                    "Topic :: Scientific/Engineering",
                    "Topic :: Scientific/Engineering :: Astronomy"],
-      url="https://github.com/mraspaud/pyorbital",
+      url="https://github.com/pytroll/pyorbital",
       test_suite='pyorbital.tests.suite',
-      package_dir = {'pyorbital': 'pyorbital'},
-      packages = ['pyorbital'],
-      install_requires=['numpy>=1.6.0,!=1.14.0'],
+      package_dir={'pyorbital': 'pyorbital'},
+      packages=['pyorbital'],
+      install_requires=['numpy>=1.11.0,!=1.14.0', 'scipy'],
       zip_safe=False,
       )


=====================================
versioneer.py
=====================================
@@ -0,0 +1,1822 @@
+
+# Version: 0.18
+
+"""The Versioneer - like a rocketeer, but for versions.
+
+The Versioneer
+==============
+
+* like a rocketeer, but for versions!
+* https://github.com/warner/python-versioneer
+* Brian Warner
+* License: Public Domain
+* Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, and pypy
+* [![Latest Version]
+(https://pypip.in/version/versioneer/badge.svg?style=flat)
+](https://pypi.python.org/pypi/versioneer/)
+* [![Build Status]
+(https://travis-ci.org/warner/python-versioneer.png?branch=master)
+](https://travis-ci.org/warner/python-versioneer)
+
+This is a tool for managing a recorded version number in distutils-based
+python projects. The goal is to remove the tedious and error-prone "update
+the embedded version string" step from your release process. Making a new
+release should be as easy as recording a new tag in your version-control
+system, and maybe making new tarballs.
+
+
+## Quick Install
+
+* `pip install versioneer` to somewhere to your $PATH
+* add a `[versioneer]` section to your setup.cfg (see below)
+* run `versioneer install` in your source tree, commit the results
+
+## Version Identifiers
+
+Source trees come from a variety of places:
+
+* a version-control system checkout (mostly used by developers)
+* a nightly tarball, produced by build automation
+* a snapshot tarball, produced by a web-based VCS browser, like github's
+  "tarball from tag" feature
+* a release tarball, produced by "setup.py sdist", distributed through PyPI
+
+Within each source tree, the version identifier (either a string or a number,
+this tool is format-agnostic) can come from a variety of places:
+
+* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows
+  about recent "tags" and an absolute revision-id
+* the name of the directory into which the tarball was unpacked
+* an expanded VCS keyword ($Id$, etc)
+* a `_version.py` created by some earlier build step
+
+For released software, the version identifier is closely related to a VCS
+tag. Some projects use tag names that include more than just the version
+string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool
+needs to strip the tag prefix to extract the version identifier. For
+unreleased software (between tags), the version identifier should provide
+enough information to help developers recreate the same tree, while also
+giving them an idea of roughly how old the tree is (after version 1.2, before
+version 1.3). Many VCS systems can report a description that captures this,
+for example `git describe --tags --dirty --always` reports things like
+"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the
+0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has
+uncommitted changes.
+
+The version identifier is used for multiple purposes:
+
+* to allow the module to self-identify its version: `myproject.__version__`
+* to choose a name and prefix for a 'setup.py sdist' tarball
+
+## Theory of Operation
+
+Versioneer works by adding a special `_version.py` file into your source
+tree, where your `__init__.py` can import it. This `_version.py` knows how to
+dynamically ask the VCS tool for version information at import time.
+
+`_version.py` also contains `$Revision$` markers, and the installation
+process marks `_version.py` to have this marker rewritten with a tag name
+during the `git archive` command. As a result, generated tarballs will
+contain enough information to get the proper version.
+
+To allow `setup.py` to compute a version too, a `versioneer.py` is added to
+the top level of your source tree, next to `setup.py` and the `setup.cfg`
+that configures it. This overrides several distutils/setuptools commands to
+compute the version when invoked, and changes `setup.py build` and `setup.py
+sdist` to replace `_version.py` with a small static file that contains just
+the generated version data.
+
+## Installation
+
+See [INSTALL.md](./INSTALL.md) for detailed installation instructions.
+
+## Version-String Flavors
+
+Code which uses Versioneer can learn about its version string at runtime by
+importing `_version` from your main `__init__.py` file and running the
+`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can
+import the top-level `versioneer.py` and run `get_versions()`.
+
+Both functions return a dictionary with different flavors of version
+information:
+
+* `['version']`: A condensed version string, rendered using the selected
+  style. This is the most commonly used value for the project's version
+  string. The default "pep440" style yields strings like `0.11`,
+  `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section
+  below for alternative styles.
+
+* `['full-revisionid']`: detailed revision identifier. For Git, this is the
+  full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac".
+
+* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the
+  commit date in ISO 8601 format. This will be None if the date is not
+  available.
+
+* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that
+  this is only accurate if run in a VCS checkout, otherwise it is likely to
+  be False or None
+
+* `['error']`: if the version string could not be computed, this will be set
+  to a string describing the problem, otherwise it will be None. It may be
+  useful to throw an exception in setup.py if this is set, to avoid e.g.
+  creating tarballs with a version string of "unknown".
+
+Some variants are more useful than others. Including `full-revisionid` in a
+bug report should allow developers to reconstruct the exact code being tested
+(or indicate the presence of local changes that should be shared with the
+developers). `version` is suitable for display in an "about" box or a CLI
+`--version` output: it can be easily compared against release notes and lists
+of bugs fixed in various releases.
+
+The installer adds the following text to your `__init__.py` to place a basic
+version in `YOURPROJECT.__version__`:
+
+    from ._version import get_versions
+    __version__ = get_versions()['version']
+    del get_versions
+
+## Styles
+
+The setup.cfg `style=` configuration controls how the VCS information is
+rendered into a version string.
+
+The default style, "pep440", produces a PEP440-compliant string, equal to the
+un-prefixed tag name for actual releases, and containing an additional "local
+version" section with more detail for in-between builds. For Git, this is
+TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags
+--dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the
+tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and
+that this commit is two revisions ("+2") beyond the "0.11" tag. For released
+software (exactly equal to a known tag), the identifier will only contain the
+stripped tag, e.g. "0.11".
+
+Other styles are available. See [details.md](details.md) in the Versioneer
+source tree for descriptions.
+
+## Debugging
+
+Versioneer tries to avoid fatal errors: if something goes wrong, it will tend
+to return a version of "0+unknown". To investigate the problem, run `setup.py
+version`, which will run the version-lookup code in a verbose mode, and will
+display the full contents of `get_versions()` (including the `error` string,
+which may help identify what went wrong).
+
+## Known Limitations
+
+Some situations are known to cause problems for Versioneer. This details the
+most significant ones. More can be found on Github
+[issues page](https://github.com/warner/python-versioneer/issues).
+
+### Subprojects
+
+Versioneer has limited support for source trees in which `setup.py` is not in
+the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are
+two common reasons why `setup.py` might not be in the root:
+
+* Source trees which contain multiple subprojects, such as
+  [Buildbot](https://github.com/buildbot/buildbot), which contains both
+  "master" and "slave" subprojects, each with their own `setup.py`,
+  `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI
+  distributions (and upload multiple independently-installable tarballs).
+* Source trees whose main purpose is to contain a C library, but which also
+  provide bindings to Python (and perhaps other langauges) in subdirectories.
+
+Versioneer will look for `.git` in parent directories, and most operations
+should get the right version string. However `pip` and `setuptools` have bugs
+and implementation details which frequently cause `pip install .` from a
+subproject directory to fail to find a correct version string (so it usually
+defaults to `0+unknown`).
+
+`pip install --editable .` should work correctly. `setup.py install` might
+work too.
+
+Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in
+some later version.
+
+[Bug #38](https://github.com/warner/python-versioneer/issues/38) is tracking
+this issue. The discussion in
+[PR #61](https://github.com/warner/python-versioneer/pull/61) describes the
+issue from the Versioneer side in more detail.
+[pip PR#3176](https://github.com/pypa/pip/pull/3176) and
+[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve
+pip to let Versioneer work correctly.
+
+Versioneer-0.16 and earlier only looked for a `.git` directory next to the
+`setup.cfg`, so subprojects were completely unsupported with those releases.
+
+### Editable installs with setuptools <= 18.5
+
+`setup.py develop` and `pip install --editable .` allow you to install a
+project into a virtualenv once, then continue editing the source code (and
+test) without re-installing after every change.
+
+"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a
+convenient way to specify executable scripts that should be installed along
+with the python package.
+
+These both work as expected when using modern setuptools. When using
+setuptools-18.5 or earlier, however, certain operations will cause
+`pkg_resources.DistributionNotFound` errors when running the entrypoint
+script, which must be resolved by re-installing the package. This happens
+when the install happens with one version, then the egg_info data is
+regenerated while a different version is checked out. Many setup.py commands
+cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into
+a different virtualenv), so this can be surprising.
+
+[Bug #83](https://github.com/warner/python-versioneer/issues/83) describes
+this one, but upgrading to a newer version of setuptools should probably
+resolve it.
+
+### Unicode version strings
+
+While Versioneer works (and is continually tested) with both Python 2 and
+Python 3, it is not entirely consistent with bytes-vs-unicode distinctions.
+Newer releases probably generate unicode version strings on py2. It's not
+clear that this is wrong, but it may be surprising for applications when then
+write these strings to a network connection or include them in bytes-oriented
+APIs like cryptographic checksums.
+
+[Bug #71](https://github.com/warner/python-versioneer/issues/71) investigates
+this question.
+
+
+## Updating Versioneer
+
+To upgrade your project to a new release of Versioneer, do the following:
+
+* install the new Versioneer (`pip install -U versioneer` or equivalent)
+* edit `setup.cfg`, if necessary, to include any new configuration settings
+  indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details.
+* re-run `versioneer install` in your source tree, to replace
+  `SRC/_version.py`
+* commit any changed files
+
+## Future Directions
+
+This tool is designed to make it easily extended to other version-control
+systems: all VCS-specific components are in separate directories like
+src/git/ . The top-level `versioneer.py` script is assembled from these
+components by running make-versioneer.py . In the future, make-versioneer.py
+will take a VCS name as an argument, and will construct a version of
+`versioneer.py` that is specific to the given VCS. It might also take the
+configuration arguments that are currently provided manually during
+installation by editing setup.py . Alternatively, it might go the other
+direction and include code from all supported VCS systems, reducing the
+number of intermediate scripts.
+
+
+## License
+
+To make Versioneer easier to embed, all its code is dedicated to the public
+domain. The `_version.py` that it creates is also in the public domain.
+Specifically, both are released under the Creative Commons "Public Domain
+Dedication" license (CC0-1.0), as described in
+https://creativecommons.org/publicdomain/zero/1.0/ .
+
+"""
+
+from __future__ import print_function
+try:
+    import configparser
+except ImportError:
+    import ConfigParser as configparser
+import errno
+import json
+import os
+import re
+import subprocess
+import sys
+
+
+class VersioneerConfig:
+    """Container for Versioneer configuration parameters."""
+
+
+def get_root():
+    """Get the project root directory.
+
+    We require that all commands are run from the project root, i.e. the
+    directory that contains setup.py, setup.cfg, and versioneer.py .
+    """
+    root = os.path.realpath(os.path.abspath(os.getcwd()))
+    setup_py = os.path.join(root, "setup.py")
+    versioneer_py = os.path.join(root, "versioneer.py")
+    if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)):
+        # allow 'python path/to/setup.py COMMAND'
+        root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0])))
+        setup_py = os.path.join(root, "setup.py")
+        versioneer_py = os.path.join(root, "versioneer.py")
+    if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)):
+        err = ("Versioneer was unable to run the project root directory. "
+               "Versioneer requires setup.py to be executed from "
+               "its immediate directory (like 'python setup.py COMMAND'), "
+               "or in a way that lets it use sys.argv[0] to find the root "
+               "(like 'python path/to/setup.py COMMAND').")
+        raise VersioneerBadRootError(err)
+    try:
+        # Certain runtime workflows (setup.py install/develop in a setuptools
+        # tree) execute all dependencies in a single python process, so
+        # "versioneer" may be imported multiple times, and python's shared
+        # module-import table will cache the first one. So we can't use
+        # os.path.dirname(__file__), as that will find whichever
+        # versioneer.py was first imported, even in later projects.
+        me = os.path.realpath(os.path.abspath(__file__))
+        me_dir = os.path.normcase(os.path.splitext(me)[0])
+        vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0])
+        if me_dir != vsr_dir:
+            print("Warning: build in %s is using versioneer.py from %s"
+                  % (os.path.dirname(me), versioneer_py))
+    except NameError:
+        pass
+    return root
+
+
+def get_config_from_root(root):
+    """Read the project setup.cfg file to determine Versioneer config."""
+    # This might raise EnvironmentError (if setup.cfg is missing), or
+    # configparser.NoSectionError (if it lacks a [versioneer] section), or
+    # configparser.NoOptionError (if it lacks "VCS="). See the docstring at
+    # the top of versioneer.py for instructions on writing your setup.cfg .
+    setup_cfg = os.path.join(root, "setup.cfg")
+    parser = configparser.SafeConfigParser()
+    with open(setup_cfg, "r") as f:
+        parser.readfp(f)
+    VCS = parser.get("versioneer", "VCS")  # mandatory
+
+    def get(parser, name):
+        if parser.has_option("versioneer", name):
+            return parser.get("versioneer", name)
+        return None
+    cfg = VersioneerConfig()
+    cfg.VCS = VCS
+    cfg.style = get(parser, "style") or ""
+    cfg.versionfile_source = get(parser, "versionfile_source")
+    cfg.versionfile_build = get(parser, "versionfile_build")
+    cfg.tag_prefix = get(parser, "tag_prefix")
+    if cfg.tag_prefix in ("''", '""'):
+        cfg.tag_prefix = ""
+    cfg.parentdir_prefix = get(parser, "parentdir_prefix")
+    cfg.verbose = get(parser, "verbose")
+    return cfg
+
+
+class NotThisMethod(Exception):
+    """Exception raised if a method is not valid for the current scenario."""
+
+
+# these dictionaries contain VCS-specific tools
+LONG_VERSION_PY = {}
+HANDLERS = {}
+
+
+def register_vcs_handler(vcs, method):  # decorator
+    """Decorator to mark a method as the handler for a particular VCS."""
+    def decorate(f):
+        """Store f in HANDLERS[vcs][method]."""
+        if vcs not in HANDLERS:
+            HANDLERS[vcs] = {}
+        HANDLERS[vcs][method] = f
+        return f
+    return decorate
+
+
+def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
+                env=None):
+    """Call the given command(s)."""
+    assert isinstance(commands, list)
+    p = None
+    for c in commands:
+        try:
+            dispcmd = str([c] + args)
+            # remember shell=False, so use git.cmd on windows, not just git
+            p = subprocess.Popen([c] + args, cwd=cwd, env=env,
+                                 stdout=subprocess.PIPE,
+                                 stderr=(subprocess.PIPE if hide_stderr
+                                         else None))
+            break
+        except EnvironmentError:
+            e = sys.exc_info()[1]
+            if e.errno == errno.ENOENT:
+                continue
+            if verbose:
+                print("unable to run %s" % dispcmd)
+                print(e)
+            return None, None
+    else:
+        if verbose:
+            print("unable to find command, tried %s" % (commands,))
+        return None, None
+    stdout = p.communicate()[0].strip()
+    if sys.version_info[0] >= 3:
+        stdout = stdout.decode()
+    if p.returncode != 0:
+        if verbose:
+            print("unable to run %s (error)" % dispcmd)
+            print("stdout was %s" % stdout)
+        return None, p.returncode
+    return stdout, p.returncode
+
+
+LONG_VERSION_PY['git'] = '''
+# This file helps to compute a version number in source trees obtained from
+# git-archive tarball (such as those provided by githubs download-from-tag
+# feature). Distribution tarballs (built by setup.py sdist) and build
+# directories (produced by setup.py build) will contain a much shorter file
+# that just contains the computed version number.
+
+# This file is released into the public domain. Generated by
+# versioneer-0.18 (https://github.com/warner/python-versioneer)
+
+"""Git implementation of _version.py."""
+
+import errno
+import os
+import re
+import subprocess
+import sys
+
+
+def get_keywords():
+    """Get the keywords needed to look up the version information."""
+    # these strings will be replaced by git during git-archive.
+    # setup.py/versioneer.py will grep for the variable names, so they must
+    # each be defined on a line of their own. _version.py will just call
+    # get_keywords().
+    git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s"
+    git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s"
+    git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s"
+    keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
+    return keywords
+
+
+class VersioneerConfig:
+    """Container for Versioneer configuration parameters."""
+
+
+def get_config():
+    """Create, populate and return the VersioneerConfig() object."""
+    # these strings are filled in when 'setup.py versioneer' creates
+    # _version.py
+    cfg = VersioneerConfig()
+    cfg.VCS = "git"
+    cfg.style = "%(STYLE)s"
+    cfg.tag_prefix = "%(TAG_PREFIX)s"
+    cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s"
+    cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s"
+    cfg.verbose = False
+    return cfg
+
+
+class NotThisMethod(Exception):
+    """Exception raised if a method is not valid for the current scenario."""
+
+
+LONG_VERSION_PY = {}
+HANDLERS = {}
+
+
+def register_vcs_handler(vcs, method):  # decorator
+    """Decorator to mark a method as the handler for a particular VCS."""
+    def decorate(f):
+        """Store f in HANDLERS[vcs][method]."""
+        if vcs not in HANDLERS:
+            HANDLERS[vcs] = {}
+        HANDLERS[vcs][method] = f
+        return f
+    return decorate
+
+
+def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
+                env=None):
+    """Call the given command(s)."""
+    assert isinstance(commands, list)
+    p = None
+    for c in commands:
+        try:
+            dispcmd = str([c] + args)
+            # remember shell=False, so use git.cmd on windows, not just git
+            p = subprocess.Popen([c] + args, cwd=cwd, env=env,
+                                 stdout=subprocess.PIPE,
+                                 stderr=(subprocess.PIPE if hide_stderr
+                                         else None))
+            break
+        except EnvironmentError:
+            e = sys.exc_info()[1]
+            if e.errno == errno.ENOENT:
+                continue
+            if verbose:
+                print("unable to run %%s" %% dispcmd)
+                print(e)
+            return None, None
+    else:
+        if verbose:
+            print("unable to find command, tried %%s" %% (commands,))
+        return None, None
+    stdout = p.communicate()[0].strip()
+    if sys.version_info[0] >= 3:
+        stdout = stdout.decode()
+    if p.returncode != 0:
+        if verbose:
+            print("unable to run %%s (error)" %% dispcmd)
+            print("stdout was %%s" %% stdout)
+        return None, p.returncode
+    return stdout, p.returncode
+
+
+def versions_from_parentdir(parentdir_prefix, root, verbose):
+    """Try to determine the version from the parent directory name.
+
+    Source tarballs conventionally unpack into a directory that includes both
+    the project name and a version string. We will also support searching up
+    two directory levels for an appropriately named parent directory
+    """
+    rootdirs = []
+
+    for i in range(3):
+        dirname = os.path.basename(root)
+        if dirname.startswith(parentdir_prefix):
+            return {"version": dirname[len(parentdir_prefix):],
+                    "full-revisionid": None,
+                    "dirty": False, "error": None, "date": None}
+        else:
+            rootdirs.append(root)
+            root = os.path.dirname(root)  # up a level
+
+    if verbose:
+        print("Tried directories %%s but none started with prefix %%s" %%
+              (str(rootdirs), parentdir_prefix))
+    raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
+
+
+ at register_vcs_handler("git", "get_keywords")
+def git_get_keywords(versionfile_abs):
+    """Extract version information from the given file."""
+    # the code embedded in _version.py can just fetch the value of these
+    # keywords. When used from setup.py, we don't want to import _version.py,
+    # so we do it with a regexp instead. This function is not used from
+    # _version.py.
+    keywords = {}
+    try:
+        f = open(versionfile_abs, "r")
+        for line in f.readlines():
+            if line.strip().startswith("git_refnames ="):
+                mo = re.search(r'=\s*"(.*)"', line)
+                if mo:
+                    keywords["refnames"] = mo.group(1)
+            if line.strip().startswith("git_full ="):
+                mo = re.search(r'=\s*"(.*)"', line)
+                if mo:
+                    keywords["full"] = mo.group(1)
+            if line.strip().startswith("git_date ="):
+                mo = re.search(r'=\s*"(.*)"', line)
+                if mo:
+                    keywords["date"] = mo.group(1)
+        f.close()
+    except EnvironmentError:
+        pass
+    return keywords
+
+
+ at register_vcs_handler("git", "keywords")
+def git_versions_from_keywords(keywords, tag_prefix, verbose):
+    """Get version information from git keywords."""
+    if not keywords:
+        raise NotThisMethod("no keywords at all, weird")
+    date = keywords.get("date")
+    if date is not None:
+        # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant
+        # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601
+        # -like" string, which we must then edit to make compliant), because
+        # it's been around since git-1.5.3, and it's too difficult to
+        # discover which version we're using, or to work around using an
+        # older one.
+        date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
+    refnames = keywords["refnames"].strip()
+    if refnames.startswith("$Format"):
+        if verbose:
+            print("keywords are unexpanded, not using")
+        raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
+    refs = set([r.strip() for r in refnames.strip("()").split(",")])
+    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
+    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
+    TAG = "tag: "
+    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
+    if not tags:
+        # Either we're using git < 1.8.3, or there really are no tags. We use
+        # a heuristic: assume all version tags have a digit. The old git %%d
+        # expansion behaves like git log --decorate=short and strips out the
+        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
+        # between branches and tags. By ignoring refnames without digits, we
+        # filter out many common branch names like "release" and
+        # "stabilization", as well as "HEAD" and "master".
+        tags = set([r for r in refs if re.search(r'\d', r)])
+        if verbose:
+            print("discarding '%%s', no digits" %% ",".join(refs - tags))
+    if verbose:
+        print("likely tags: %%s" %% ",".join(sorted(tags)))
+    for ref in sorted(tags):
+        # sorting will prefer e.g. "2.0" over "2.0rc1"
+        if ref.startswith(tag_prefix):
+            r = ref[len(tag_prefix):]
+            if verbose:
+                print("picking %%s" %% r)
+            return {"version": r,
+                    "full-revisionid": keywords["full"].strip(),
+                    "dirty": False, "error": None,
+                    "date": date}
+    # no suitable tags, so version is "0+unknown", but full hex is still there
+    if verbose:
+        print("no suitable tags, using unknown + full revision id")
+    return {"version": "0+unknown",
+            "full-revisionid": keywords["full"].strip(),
+            "dirty": False, "error": "no suitable tags", "date": None}
+
+
+ at register_vcs_handler("git", "pieces_from_vcs")
+def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
+    """Get version from 'git describe' in the root of the source tree.
+
+    This only gets called if the git-archive 'subst' keywords were *not*
+    expanded, and _version.py hasn't already been rewritten with a short
+    version string, meaning we're inside a checked out source tree.
+    """
+    GITS = ["git"]
+    if sys.platform == "win32":
+        GITS = ["git.cmd", "git.exe"]
+
+    out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
+                          hide_stderr=True)
+    if rc != 0:
+        if verbose:
+            print("Directory %%s not under git control" %% root)
+        raise NotThisMethod("'git rev-parse --git-dir' returned error")
+
+    # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
+    # if there isn't one, this yields HEX[-dirty] (no NUM)
+    describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
+                                          "--always", "--long",
+                                          "--match", "%%s*" %% tag_prefix],
+                                   cwd=root)
+    # --long was added in git-1.5.5
+    if describe_out is None:
+        raise NotThisMethod("'git describe' failed")
+    describe_out = describe_out.strip()
+    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
+    if full_out is None:
+        raise NotThisMethod("'git rev-parse' failed")
+    full_out = full_out.strip()
+
+    pieces = {}
+    pieces["long"] = full_out
+    pieces["short"] = full_out[:7]  # maybe improved later
+    pieces["error"] = None
+
+    # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
+    # TAG might have hyphens.
+    git_describe = describe_out
+
+    # look for -dirty suffix
+    dirty = git_describe.endswith("-dirty")
+    pieces["dirty"] = dirty
+    if dirty:
+        git_describe = git_describe[:git_describe.rindex("-dirty")]
+
+    # now we have TAG-NUM-gHEX or HEX
+
+    if "-" in git_describe:
+        # TAG-NUM-gHEX
+        mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
+        if not mo:
+            # unparseable. Maybe git-describe is misbehaving?
+            pieces["error"] = ("unable to parse git-describe output: '%%s'"
+                               %% describe_out)
+            return pieces
+
+        # tag
+        full_tag = mo.group(1)
+        if not full_tag.startswith(tag_prefix):
+            if verbose:
+                fmt = "tag '%%s' doesn't start with prefix '%%s'"
+                print(fmt %% (full_tag, tag_prefix))
+            pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'"
+                               %% (full_tag, tag_prefix))
+            return pieces
+        pieces["closest-tag"] = full_tag[len(tag_prefix):]
+
+        # distance: number of commits since tag
+        pieces["distance"] = int(mo.group(2))
+
+        # commit: short hex revision ID
+        pieces["short"] = mo.group(3)
+
+    else:
+        # HEX: no tags
+        pieces["closest-tag"] = None
+        count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
+                                    cwd=root)
+        pieces["distance"] = int(count_out)  # total number of commits
+
+    # commit date: see ISO-8601 comment in git_versions_from_keywords()
+    date = run_command(GITS, ["show", "-s", "--format=%%ci", "HEAD"],
+                       cwd=root)[0].strip()
+    pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
+
+    return pieces
+
+
+def plus_or_dot(pieces):
+    """Return a + if we don't already have one, else return a ."""
+    if "+" in pieces.get("closest-tag", ""):
+        return "."
+    return "+"
+
+
+def render_pep440(pieces):
+    """Build up version string, with post-release "local version identifier".
+
+    Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
+    get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
+
+    Exceptions:
+    1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += plus_or_dot(pieces)
+            rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"])
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"],
+                                          pieces["short"])
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
+def render_pep440_pre(pieces):
+    """TAG[.post.devDISTANCE] -- No -dirty.
+
+    Exceptions:
+    1: no tags. 0.post.devDISTANCE
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"]:
+            rendered += ".post.dev%%d" %% pieces["distance"]
+    else:
+        # exception #1
+        rendered = "0.post.dev%%d" %% pieces["distance"]
+    return rendered
+
+
+def render_pep440_post(pieces):
+    """TAG[.postDISTANCE[.dev0]+gHEX] .
+
+    The ".dev0" means dirty. Note that .dev0 sorts backwards
+    (a dirty tree will appear "older" than the corresponding clean one),
+    but you shouldn't be releasing software with -dirty anyways.
+
+    Exceptions:
+    1: no tags. 0.postDISTANCE[.dev0]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%%d" %% pieces["distance"]
+            if pieces["dirty"]:
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "g%%s" %% pieces["short"]
+    else:
+        # exception #1
+        rendered = "0.post%%d" %% pieces["distance"]
+        if pieces["dirty"]:
+            rendered += ".dev0"
+        rendered += "+g%%s" %% pieces["short"]
+    return rendered
+
+
+def render_pep440_old(pieces):
+    """TAG[.postDISTANCE[.dev0]] .
+
+    The ".dev0" means dirty.
+
+    Eexceptions:
+    1: no tags. 0.postDISTANCE[.dev0]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%%d" %% pieces["distance"]
+            if pieces["dirty"]:
+                rendered += ".dev0"
+    else:
+        # exception #1
+        rendered = "0.post%%d" %% pieces["distance"]
+        if pieces["dirty"]:
+            rendered += ".dev0"
+    return rendered
+
+
+def render_git_describe(pieces):
+    """TAG[-DISTANCE-gHEX][-dirty].
+
+    Like 'git describe --tags --dirty --always'.
+
+    Exceptions:
+    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"]:
+            rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
+    else:
+        # exception #1
+        rendered = pieces["short"]
+    if pieces["dirty"]:
+        rendered += "-dirty"
+    return rendered
+
+
+def render_git_describe_long(pieces):
+    """TAG-DISTANCE-gHEX[-dirty].
+
+    Like 'git describe --tags --dirty --always -long'.
+    The distance/hash is unconditional.
+
+    Exceptions:
+    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
+    else:
+        # exception #1
+        rendered = pieces["short"]
+    if pieces["dirty"]:
+        rendered += "-dirty"
+    return rendered
+
+
+def render(pieces, style):
+    """Render the given version pieces into the requested style."""
+    if pieces["error"]:
+        return {"version": "unknown",
+                "full-revisionid": pieces.get("long"),
+                "dirty": None,
+                "error": pieces["error"],
+                "date": None}
+
+    if not style or style == "default":
+        style = "pep440"  # the default
+
+    if style == "pep440":
+        rendered = render_pep440(pieces)
+    elif style == "pep440-pre":
+        rendered = render_pep440_pre(pieces)
+    elif style == "pep440-post":
+        rendered = render_pep440_post(pieces)
+    elif style == "pep440-old":
+        rendered = render_pep440_old(pieces)
+    elif style == "git-describe":
+        rendered = render_git_describe(pieces)
+    elif style == "git-describe-long":
+        rendered = render_git_describe_long(pieces)
+    else:
+        raise ValueError("unknown style '%%s'" %% style)
+
+    return {"version": rendered, "full-revisionid": pieces["long"],
+            "dirty": pieces["dirty"], "error": None,
+            "date": pieces.get("date")}
+
+
+def get_versions():
+    """Get version information or return default if unable to do so."""
+    # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
+    # __file__, we can work backwards from there to the root. Some
+    # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
+    # case we can only use expanded keywords.
+
+    cfg = get_config()
+    verbose = cfg.verbose
+
+    try:
+        return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
+                                          verbose)
+    except NotThisMethod:
+        pass
+
+    try:
+        root = os.path.realpath(__file__)
+        # versionfile_source is the relative path from the top of the source
+        # tree (where the .git directory might live) to this file. Invert
+        # this to find the root from __file__.
+        for i in cfg.versionfile_source.split('/'):
+            root = os.path.dirname(root)
+    except NameError:
+        return {"version": "0+unknown", "full-revisionid": None,
+                "dirty": None,
+                "error": "unable to find root of source tree",
+                "date": None}
+
+    try:
+        pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
+        return render(pieces, cfg.style)
+    except NotThisMethod:
+        pass
+
+    try:
+        if cfg.parentdir_prefix:
+            return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
+    except NotThisMethod:
+        pass
+
+    return {"version": "0+unknown", "full-revisionid": None,
+            "dirty": None,
+            "error": "unable to compute version", "date": None}
+'''
+
+
+ at register_vcs_handler("git", "get_keywords")
+def git_get_keywords(versionfile_abs):
+    """Extract version information from the given file."""
+    # the code embedded in _version.py can just fetch the value of these
+    # keywords. When used from setup.py, we don't want to import _version.py,
+    # so we do it with a regexp instead. This function is not used from
+    # _version.py.
+    keywords = {}
+    try:
+        f = open(versionfile_abs, "r")
+        for line in f.readlines():
+            if line.strip().startswith("git_refnames ="):
+                mo = re.search(r'=\s*"(.*)"', line)
+                if mo:
+                    keywords["refnames"] = mo.group(1)
+            if line.strip().startswith("git_full ="):
+                mo = re.search(r'=\s*"(.*)"', line)
+                if mo:
+                    keywords["full"] = mo.group(1)
+            if line.strip().startswith("git_date ="):
+                mo = re.search(r'=\s*"(.*)"', line)
+                if mo:
+                    keywords["date"] = mo.group(1)
+        f.close()
+    except EnvironmentError:
+        pass
+    return keywords
+
+
+ at register_vcs_handler("git", "keywords")
+def git_versions_from_keywords(keywords, tag_prefix, verbose):
+    """Get version information from git keywords."""
+    if not keywords:
+        raise NotThisMethod("no keywords at all, weird")
+    date = keywords.get("date")
+    if date is not None:
+        # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
+        # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
+        # -like" string, which we must then edit to make compliant), because
+        # it's been around since git-1.5.3, and it's too difficult to
+        # discover which version we're using, or to work around using an
+        # older one.
+        date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
+    refnames = keywords["refnames"].strip()
+    if refnames.startswith("$Format"):
+        if verbose:
+            print("keywords are unexpanded, not using")
+        raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
+    refs = set([r.strip() for r in refnames.strip("()").split(",")])
+    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
+    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
+    TAG = "tag: "
+    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
+    if not tags:
+        # Either we're using git < 1.8.3, or there really are no tags. We use
+        # a heuristic: assume all version tags have a digit. The old git %d
+        # expansion behaves like git log --decorate=short and strips out the
+        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
+        # between branches and tags. By ignoring refnames without digits, we
+        # filter out many common branch names like "release" and
+        # "stabilization", as well as "HEAD" and "master".
+        tags = set([r for r in refs if re.search(r'\d', r)])
+        if verbose:
+            print("discarding '%s', no digits" % ",".join(refs - tags))
+    if verbose:
+        print("likely tags: %s" % ",".join(sorted(tags)))
+    for ref in sorted(tags):
+        # sorting will prefer e.g. "2.0" over "2.0rc1"
+        if ref.startswith(tag_prefix):
+            r = ref[len(tag_prefix):]
+            if verbose:
+                print("picking %s" % r)
+            return {"version": r,
+                    "full-revisionid": keywords["full"].strip(),
+                    "dirty": False, "error": None,
+                    "date": date}
+    # no suitable tags, so version is "0+unknown", but full hex is still there
+    if verbose:
+        print("no suitable tags, using unknown + full revision id")
+    return {"version": "0+unknown",
+            "full-revisionid": keywords["full"].strip(),
+            "dirty": False, "error": "no suitable tags", "date": None}
+
+
+ at register_vcs_handler("git", "pieces_from_vcs")
+def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
+    """Get version from 'git describe' in the root of the source tree.
+
+    This only gets called if the git-archive 'subst' keywords were *not*
+    expanded, and _version.py hasn't already been rewritten with a short
+    version string, meaning we're inside a checked out source tree.
+    """
+    GITS = ["git"]
+    if sys.platform == "win32":
+        GITS = ["git.cmd", "git.exe"]
+
+    out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
+                          hide_stderr=True)
+    if rc != 0:
+        if verbose:
+            print("Directory %s not under git control" % root)
+        raise NotThisMethod("'git rev-parse --git-dir' returned error")
+
+    # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
+    # if there isn't one, this yields HEX[-dirty] (no NUM)
+    describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
+                                          "--always", "--long",
+                                          "--match", "%s*" % tag_prefix],
+                                   cwd=root)
+    # --long was added in git-1.5.5
+    if describe_out is None:
+        raise NotThisMethod("'git describe' failed")
+    describe_out = describe_out.strip()
+    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
+    if full_out is None:
+        raise NotThisMethod("'git rev-parse' failed")
+    full_out = full_out.strip()
+
+    pieces = {}
+    pieces["long"] = full_out
+    pieces["short"] = full_out[:7]  # maybe improved later
+    pieces["error"] = None
+
+    # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
+    # TAG might have hyphens.
+    git_describe = describe_out
+
+    # look for -dirty suffix
+    dirty = git_describe.endswith("-dirty")
+    pieces["dirty"] = dirty
+    if dirty:
+        git_describe = git_describe[:git_describe.rindex("-dirty")]
+
+    # now we have TAG-NUM-gHEX or HEX
+
+    if "-" in git_describe:
+        # TAG-NUM-gHEX
+        mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
+        if not mo:
+            # unparseable. Maybe git-describe is misbehaving?
+            pieces["error"] = ("unable to parse git-describe output: '%s'"
+                               % describe_out)
+            return pieces
+
+        # tag
+        full_tag = mo.group(1)
+        if not full_tag.startswith(tag_prefix):
+            if verbose:
+                fmt = "tag '%s' doesn't start with prefix '%s'"
+                print(fmt % (full_tag, tag_prefix))
+            pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
+                               % (full_tag, tag_prefix))
+            return pieces
+        pieces["closest-tag"] = full_tag[len(tag_prefix):]
+
+        # distance: number of commits since tag
+        pieces["distance"] = int(mo.group(2))
+
+        # commit: short hex revision ID
+        pieces["short"] = mo.group(3)
+
+    else:
+        # HEX: no tags
+        pieces["closest-tag"] = None
+        count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
+                                    cwd=root)
+        pieces["distance"] = int(count_out)  # total number of commits
+
+    # commit date: see ISO-8601 comment in git_versions_from_keywords()
+    date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"],
+                       cwd=root)[0].strip()
+    pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
+
+    return pieces
+
+
+def do_vcs_install(manifest_in, versionfile_source, ipy):
+    """Git-specific installation logic for Versioneer.
+
+    For Git, this means creating/changing .gitattributes to mark _version.py
+    for export-subst keyword substitution.
+    """
+    GITS = ["git"]
+    if sys.platform == "win32":
+        GITS = ["git.cmd", "git.exe"]
+    files = [manifest_in, versionfile_source]
+    if ipy:
+        files.append(ipy)
+    try:
+        me = __file__
+        if me.endswith(".pyc") or me.endswith(".pyo"):
+            me = os.path.splitext(me)[0] + ".py"
+        versioneer_file = os.path.relpath(me)
+    except NameError:
+        versioneer_file = "versioneer.py"
+    files.append(versioneer_file)
+    present = False
+    try:
+        f = open(".gitattributes", "r")
+        for line in f.readlines():
+            if line.strip().startswith(versionfile_source):
+                if "export-subst" in line.strip().split()[1:]:
+                    present = True
+        f.close()
+    except EnvironmentError:
+        pass
+    if not present:
+        f = open(".gitattributes", "a+")
+        f.write("%s export-subst\n" % versionfile_source)
+        f.close()
+        files.append(".gitattributes")
+    run_command(GITS, ["add", "--"] + files)
+
+
+def versions_from_parentdir(parentdir_prefix, root, verbose):
+    """Try to determine the version from the parent directory name.
+
+    Source tarballs conventionally unpack into a directory that includes both
+    the project name and a version string. We will also support searching up
+    two directory levels for an appropriately named parent directory
+    """
+    rootdirs = []
+
+    for i in range(3):
+        dirname = os.path.basename(root)
+        if dirname.startswith(parentdir_prefix):
+            return {"version": dirname[len(parentdir_prefix):],
+                    "full-revisionid": None,
+                    "dirty": False, "error": None, "date": None}
+        else:
+            rootdirs.append(root)
+            root = os.path.dirname(root)  # up a level
+
+    if verbose:
+        print("Tried directories %s but none started with prefix %s" %
+              (str(rootdirs), parentdir_prefix))
+    raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
+
+
+SHORT_VERSION_PY = """
+# This file was generated by 'versioneer.py' (0.18) from
+# revision-control system data, or from the parent directory name of an
+# unpacked source archive. Distribution tarballs contain a pre-generated copy
+# of this file.
+
+import json
+
+version_json = '''
+%s
+'''  # END VERSION_JSON
+
+
+def get_versions():
+    return json.loads(version_json)
+"""
+
+
+def versions_from_file(filename):
+    """Try to determine the version from _version.py if present."""
+    try:
+        with open(filename) as f:
+            contents = f.read()
+    except EnvironmentError:
+        raise NotThisMethod("unable to read _version.py")
+    mo = re.search(r"version_json = '''\n(.*)'''  # END VERSION_JSON",
+                   contents, re.M | re.S)
+    if not mo:
+        mo = re.search(r"version_json = '''\r\n(.*)'''  # END VERSION_JSON",
+                       contents, re.M | re.S)
+    if not mo:
+        raise NotThisMethod("no version_json in _version.py")
+    return json.loads(mo.group(1))
+
+
+def write_to_version_file(filename, versions):
+    """Write the given version number to the given _version.py file."""
+    os.unlink(filename)
+    contents = json.dumps(versions, sort_keys=True,
+                          indent=1, separators=(",", ": "))
+    with open(filename, "w") as f:
+        f.write(SHORT_VERSION_PY % contents)
+
+    print("set %s to '%s'" % (filename, versions["version"]))
+
+
+def plus_or_dot(pieces):
+    """Return a + if we don't already have one, else return a ."""
+    if "+" in pieces.get("closest-tag", ""):
+        return "."
+    return "+"
+
+
+def render_pep440(pieces):
+    """Build up version string, with post-release "local version identifier".
+
+    Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
+    get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
+
+    Exceptions:
+    1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += plus_or_dot(pieces)
+            rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
+            if pieces["dirty"]:
+                rendered += ".dirty"
+    else:
+        # exception #1
+        rendered = "0+untagged.%d.g%s" % (pieces["distance"],
+                                          pieces["short"])
+        if pieces["dirty"]:
+            rendered += ".dirty"
+    return rendered
+
+
+def render_pep440_pre(pieces):
+    """TAG[.post.devDISTANCE] -- No -dirty.
+
+    Exceptions:
+    1: no tags. 0.post.devDISTANCE
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"]:
+            rendered += ".post.dev%d" % pieces["distance"]
+    else:
+        # exception #1
+        rendered = "0.post.dev%d" % pieces["distance"]
+    return rendered
+
+
+def render_pep440_post(pieces):
+    """TAG[.postDISTANCE[.dev0]+gHEX] .
+
+    The ".dev0" means dirty. Note that .dev0 sorts backwards
+    (a dirty tree will appear "older" than the corresponding clean one),
+    but you shouldn't be releasing software with -dirty anyways.
+
+    Exceptions:
+    1: no tags. 0.postDISTANCE[.dev0]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%d" % pieces["distance"]
+            if pieces["dirty"]:
+                rendered += ".dev0"
+            rendered += plus_or_dot(pieces)
+            rendered += "g%s" % pieces["short"]
+    else:
+        # exception #1
+        rendered = "0.post%d" % pieces["distance"]
+        if pieces["dirty"]:
+            rendered += ".dev0"
+        rendered += "+g%s" % pieces["short"]
+    return rendered
+
+
+def render_pep440_old(pieces):
+    """TAG[.postDISTANCE[.dev0]] .
+
+    The ".dev0" means dirty.
+
+    Eexceptions:
+    1: no tags. 0.postDISTANCE[.dev0]
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"] or pieces["dirty"]:
+            rendered += ".post%d" % pieces["distance"]
+            if pieces["dirty"]:
+                rendered += ".dev0"
+    else:
+        # exception #1
+        rendered = "0.post%d" % pieces["distance"]
+        if pieces["dirty"]:
+            rendered += ".dev0"
+    return rendered
+
+
+def render_git_describe(pieces):
+    """TAG[-DISTANCE-gHEX][-dirty].
+
+    Like 'git describe --tags --dirty --always'.
+
+    Exceptions:
+    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        if pieces["distance"]:
+            rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
+    else:
+        # exception #1
+        rendered = pieces["short"]
+    if pieces["dirty"]:
+        rendered += "-dirty"
+    return rendered
+
+
+def render_git_describe_long(pieces):
+    """TAG-DISTANCE-gHEX[-dirty].
+
+    Like 'git describe --tags --dirty --always -long'.
+    The distance/hash is unconditional.
+
+    Exceptions:
+    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
+    """
+    if pieces["closest-tag"]:
+        rendered = pieces["closest-tag"]
+        rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
+    else:
+        # exception #1
+        rendered = pieces["short"]
+    if pieces["dirty"]:
+        rendered += "-dirty"
+    return rendered
+
+
+def render(pieces, style):
+    """Render the given version pieces into the requested style."""
+    if pieces["error"]:
+        return {"version": "unknown",
+                "full-revisionid": pieces.get("long"),
+                "dirty": None,
+                "error": pieces["error"],
+                "date": None}
+
+    if not style or style == "default":
+        style = "pep440"  # the default
+
+    if style == "pep440":
+        rendered = render_pep440(pieces)
+    elif style == "pep440-pre":
+        rendered = render_pep440_pre(pieces)
+    elif style == "pep440-post":
+        rendered = render_pep440_post(pieces)
+    elif style == "pep440-old":
+        rendered = render_pep440_old(pieces)
+    elif style == "git-describe":
+        rendered = render_git_describe(pieces)
+    elif style == "git-describe-long":
+        rendered = render_git_describe_long(pieces)
+    else:
+        raise ValueError("unknown style '%s'" % style)
+
+    return {"version": rendered, "full-revisionid": pieces["long"],
+            "dirty": pieces["dirty"], "error": None,
+            "date": pieces.get("date")}
+
+
+class VersioneerBadRootError(Exception):
+    """The project root directory is unknown or missing key files."""
+
+
+def get_versions(verbose=False):
+    """Get the project version from whatever source is available.
+
+    Returns dict with two keys: 'version' and 'full'.
+    """
+    if "versioneer" in sys.modules:
+        # see the discussion in cmdclass.py:get_cmdclass()
+        del sys.modules["versioneer"]
+
+    root = get_root()
+    cfg = get_config_from_root(root)
+
+    assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg"
+    handlers = HANDLERS.get(cfg.VCS)
+    assert handlers, "unrecognized VCS '%s'" % cfg.VCS
+    verbose = verbose or cfg.verbose
+    assert cfg.versionfile_source is not None, \
+        "please set versioneer.versionfile_source"
+    assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix"
+
+    versionfile_abs = os.path.join(root, cfg.versionfile_source)
+
+    # extract version from first of: _version.py, VCS command (e.g. 'git
+    # describe'), parentdir. This is meant to work for developers using a
+    # source checkout, for users of a tarball created by 'setup.py sdist',
+    # and for users of a tarball/zipball created by 'git archive' or github's
+    # download-from-tag feature or the equivalent in other VCSes.
+
+    get_keywords_f = handlers.get("get_keywords")
+    from_keywords_f = handlers.get("keywords")
+    if get_keywords_f and from_keywords_f:
+        try:
+            keywords = get_keywords_f(versionfile_abs)
+            ver = from_keywords_f(keywords, cfg.tag_prefix, verbose)
+            if verbose:
+                print("got version from expanded keyword %s" % ver)
+            return ver
+        except NotThisMethod:
+            pass
+
+    try:
+        ver = versions_from_file(versionfile_abs)
+        if verbose:
+            print("got version from file %s %s" % (versionfile_abs, ver))
+        return ver
+    except NotThisMethod:
+        pass
+
+    from_vcs_f = handlers.get("pieces_from_vcs")
+    if from_vcs_f:
+        try:
+            pieces = from_vcs_f(cfg.tag_prefix, root, verbose)
+            ver = render(pieces, cfg.style)
+            if verbose:
+                print("got version from VCS %s" % ver)
+            return ver
+        except NotThisMethod:
+            pass
+
+    try:
+        if cfg.parentdir_prefix:
+            ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
+            if verbose:
+                print("got version from parentdir %s" % ver)
+            return ver
+    except NotThisMethod:
+        pass
+
+    if verbose:
+        print("unable to compute version")
+
+    return {"version": "0+unknown", "full-revisionid": None,
+            "dirty": None, "error": "unable to compute version",
+            "date": None}
+
+
+def get_version():
+    """Get the short version string for this project."""
+    return get_versions()["version"]
+
+
+def get_cmdclass():
+    """Get the custom setuptools/distutils subclasses used by Versioneer."""
+    if "versioneer" in sys.modules:
+        del sys.modules["versioneer"]
+        # this fixes the "python setup.py develop" case (also 'install' and
+        # 'easy_install .'), in which subdependencies of the main project are
+        # built (using setup.py bdist_egg) in the same python process. Assume
+        # a main project A and a dependency B, which use different versions
+        # of Versioneer. A's setup.py imports A's Versioneer, leaving it in
+        # sys.modules by the time B's setup.py is executed, causing B to run
+        # with the wrong versioneer. Setuptools wraps the sub-dep builds in a
+        # sandbox that restores sys.modules to it's pre-build state, so the
+        # parent is protected against the child's "import versioneer". By
+        # removing ourselves from sys.modules here, before the child build
+        # happens, we protect the child from the parent's versioneer too.
+        # Also see https://github.com/warner/python-versioneer/issues/52
+
+    cmds = {}
+
+    # we add "version" to both distutils and setuptools
+    from distutils.core import Command
+
+    class cmd_version(Command):
+        description = "report generated version string"
+        user_options = []
+        boolean_options = []
+
+        def initialize_options(self):
+            pass
+
+        def finalize_options(self):
+            pass
+
+        def run(self):
+            vers = get_versions(verbose=True)
+            print("Version: %s" % vers["version"])
+            print(" full-revisionid: %s" % vers.get("full-revisionid"))
+            print(" dirty: %s" % vers.get("dirty"))
+            print(" date: %s" % vers.get("date"))
+            if vers["error"]:
+                print(" error: %s" % vers["error"])
+    cmds["version"] = cmd_version
+
+    # we override "build_py" in both distutils and setuptools
+    #
+    # most invocation pathways end up running build_py:
+    #  distutils/build -> build_py
+    #  distutils/install -> distutils/build ->..
+    #  setuptools/bdist_wheel -> distutils/install ->..
+    #  setuptools/bdist_egg -> distutils/install_lib -> build_py
+    #  setuptools/install -> bdist_egg ->..
+    #  setuptools/develop -> ?
+    #  pip install:
+    #   copies source tree to a tempdir before running egg_info/etc
+    #   if .git isn't copied too, 'git describe' will fail
+    #   then does setup.py bdist_wheel, or sometimes setup.py install
+    #  setup.py egg_info -> ?
+
+    # we override different "build_py" commands for both environments
+    if "setuptools" in sys.modules:
+        from setuptools.command.build_py import build_py as _build_py
+    else:
+        from distutils.command.build_py import build_py as _build_py
+
+    class cmd_build_py(_build_py):
+        def run(self):
+            root = get_root()
+            cfg = get_config_from_root(root)
+            versions = get_versions()
+            _build_py.run(self)
+            # now locate _version.py in the new build/ directory and replace
+            # it with an updated value
+            if cfg.versionfile_build:
+                target_versionfile = os.path.join(self.build_lib,
+                                                  cfg.versionfile_build)
+                print("UPDATING %s" % target_versionfile)
+                write_to_version_file(target_versionfile, versions)
+    cmds["build_py"] = cmd_build_py
+
+    if "cx_Freeze" in sys.modules:  # cx_freeze enabled?
+        from cx_Freeze.dist import build_exe as _build_exe
+        # nczeczulin reports that py2exe won't like the pep440-style string
+        # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g.
+        # setup(console=[{
+        #   "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION
+        #   "product_version": versioneer.get_version(),
+        #   ...
+
+        class cmd_build_exe(_build_exe):
+            def run(self):
+                root = get_root()
+                cfg = get_config_from_root(root)
+                versions = get_versions()
+                target_versionfile = cfg.versionfile_source
+                print("UPDATING %s" % target_versionfile)
+                write_to_version_file(target_versionfile, versions)
+
+                _build_exe.run(self)
+                os.unlink(target_versionfile)
+                with open(cfg.versionfile_source, "w") as f:
+                    LONG = LONG_VERSION_PY[cfg.VCS]
+                    f.write(LONG %
+                            {"DOLLAR": "$",
+                             "STYLE": cfg.style,
+                             "TAG_PREFIX": cfg.tag_prefix,
+                             "PARENTDIR_PREFIX": cfg.parentdir_prefix,
+                             "VERSIONFILE_SOURCE": cfg.versionfile_source,
+                             })
+        cmds["build_exe"] = cmd_build_exe
+        del cmds["build_py"]
+
+    if 'py2exe' in sys.modules:  # py2exe enabled?
+        try:
+            from py2exe.distutils_buildexe import py2exe as _py2exe  # py3
+        except ImportError:
+            from py2exe.build_exe import py2exe as _py2exe  # py2
+
+        class cmd_py2exe(_py2exe):
+            def run(self):
+                root = get_root()
+                cfg = get_config_from_root(root)
+                versions = get_versions()
+                target_versionfile = cfg.versionfile_source
+                print("UPDATING %s" % target_versionfile)
+                write_to_version_file(target_versionfile, versions)
+
+                _py2exe.run(self)
+                os.unlink(target_versionfile)
+                with open(cfg.versionfile_source, "w") as f:
+                    LONG = LONG_VERSION_PY[cfg.VCS]
+                    f.write(LONG %
+                            {"DOLLAR": "$",
+                             "STYLE": cfg.style,
+                             "TAG_PREFIX": cfg.tag_prefix,
+                             "PARENTDIR_PREFIX": cfg.parentdir_prefix,
+                             "VERSIONFILE_SOURCE": cfg.versionfile_source,
+                             })
+        cmds["py2exe"] = cmd_py2exe
+
+    # we override different "sdist" commands for both environments
+    if "setuptools" in sys.modules:
+        from setuptools.command.sdist import sdist as _sdist
+    else:
+        from distutils.command.sdist import sdist as _sdist
+
+    class cmd_sdist(_sdist):
+        def run(self):
+            versions = get_versions()
+            self._versioneer_generated_versions = versions
+            # unless we update this, the command will keep using the old
+            # version
+            self.distribution.metadata.version = versions["version"]
+            return _sdist.run(self)
+
+        def make_release_tree(self, base_dir, files):
+            root = get_root()
+            cfg = get_config_from_root(root)
+            _sdist.make_release_tree(self, base_dir, files)
+            # now locate _version.py in the new base_dir directory
+            # (remembering that it may be a hardlink) and replace it with an
+            # updated value
+            target_versionfile = os.path.join(base_dir, cfg.versionfile_source)
+            print("UPDATING %s" % target_versionfile)
+            write_to_version_file(target_versionfile,
+                                  self._versioneer_generated_versions)
+    cmds["sdist"] = cmd_sdist
+
+    return cmds
+
+
+CONFIG_ERROR = """
+setup.cfg is missing the necessary Versioneer configuration. You need
+a section like:
+
+ [versioneer]
+ VCS = git
+ style = pep440
+ versionfile_source = src/myproject/_version.py
+ versionfile_build = myproject/_version.py
+ tag_prefix =
+ parentdir_prefix = myproject-
+
+You will also need to edit your setup.py to use the results:
+
+ import versioneer
+ setup(version=versioneer.get_version(),
+       cmdclass=versioneer.get_cmdclass(), ...)
+
+Please read the docstring in ./versioneer.py for configuration instructions,
+edit setup.cfg, and re-run the installer or 'python versioneer.py setup'.
+"""
+
+SAMPLE_CONFIG = """
+# See the docstring in versioneer.py for instructions. Note that you must
+# re-run 'versioneer.py setup' after changing this section, and commit the
+# resulting files.
+
+[versioneer]
+#VCS = git
+#style = pep440
+#versionfile_source =
+#versionfile_build =
+#tag_prefix =
+#parentdir_prefix =
+
+"""
+
+INIT_PY_SNIPPET = """
+from ._version import get_versions
+__version__ = get_versions()['version']
+del get_versions
+"""
+
+
+def do_setup():
+    """Main VCS-independent setup function for installing Versioneer."""
+    root = get_root()
+    try:
+        cfg = get_config_from_root(root)
+    except (EnvironmentError, configparser.NoSectionError,
+            configparser.NoOptionError) as e:
+        if isinstance(e, (EnvironmentError, configparser.NoSectionError)):
+            print("Adding sample versioneer config to setup.cfg",
+                  file=sys.stderr)
+            with open(os.path.join(root, "setup.cfg"), "a") as f:
+                f.write(SAMPLE_CONFIG)
+        print(CONFIG_ERROR, file=sys.stderr)
+        return 1
+
+    print(" creating %s" % cfg.versionfile_source)
+    with open(cfg.versionfile_source, "w") as f:
+        LONG = LONG_VERSION_PY[cfg.VCS]
+        f.write(LONG % {"DOLLAR": "$",
+                        "STYLE": cfg.style,
+                        "TAG_PREFIX": cfg.tag_prefix,
+                        "PARENTDIR_PREFIX": cfg.parentdir_prefix,
+                        "VERSIONFILE_SOURCE": cfg.versionfile_source,
+                        })
+
+    ipy = os.path.join(os.path.dirname(cfg.versionfile_source),
+                       "__init__.py")
+    if os.path.exists(ipy):
+        try:
+            with open(ipy, "r") as f:
+                old = f.read()
+        except EnvironmentError:
+            old = ""
+        if INIT_PY_SNIPPET not in old:
+            print(" appending to %s" % ipy)
+            with open(ipy, "a") as f:
+                f.write(INIT_PY_SNIPPET)
+        else:
+            print(" %s unmodified" % ipy)
+    else:
+        print(" %s doesn't exist, ok" % ipy)
+        ipy = None
+
+    # Make sure both the top-level "versioneer.py" and versionfile_source
+    # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so
+    # they'll be copied into source distributions. Pip won't be able to
+    # install the package without this.
+    manifest_in = os.path.join(root, "MANIFEST.in")
+    simple_includes = set()
+    try:
+        with open(manifest_in, "r") as f:
+            for line in f:
+                if line.startswith("include "):
+                    for include in line.split()[1:]:
+                        simple_includes.add(include)
+    except EnvironmentError:
+        pass
+    # That doesn't cover everything MANIFEST.in can do
+    # (http://docs.python.org/2/distutils/sourcedist.html#commands), so
+    # it might give some false negatives. Appending redundant 'include'
+    # lines is safe, though.
+    if "versioneer.py" not in simple_includes:
+        print(" appending 'versioneer.py' to MANIFEST.in")
+        with open(manifest_in, "a") as f:
+            f.write("include versioneer.py\n")
+    else:
+        print(" 'versioneer.py' already in MANIFEST.in")
+    if cfg.versionfile_source not in simple_includes:
+        print(" appending versionfile_source ('%s') to MANIFEST.in" %
+              cfg.versionfile_source)
+        with open(manifest_in, "a") as f:
+            f.write("include %s\n" % cfg.versionfile_source)
+    else:
+        print(" versionfile_source already in MANIFEST.in")
+
+    # Make VCS-specific changes. For git, this means creating/changing
+    # .gitattributes to mark _version.py for export-subst keyword
+    # substitution.
+    do_vcs_install(manifest_in, cfg.versionfile_source, ipy)
+    return 0
+
+
+def scan_setup_py():
+    """Validate the contents of setup.py against Versioneer's expectations."""
+    found = set()
+    setters = False
+    errors = 0
+    with open("setup.py", "r") as f:
+        for line in f.readlines():
+            if "import versioneer" in line:
+                found.add("import")
+            if "versioneer.get_cmdclass()" in line:
+                found.add("cmdclass")
+            if "versioneer.get_version()" in line:
+                found.add("get_version")
+            if "versioneer.VCS" in line:
+                setters = True
+            if "versioneer.versionfile_source" in line:
+                setters = True
+    if len(found) != 3:
+        print("")
+        print("Your setup.py appears to be missing some important items")
+        print("(but I might be wrong). Please make sure it has something")
+        print("roughly like the following:")
+        print("")
+        print(" import versioneer")
+        print(" setup( version=versioneer.get_version(),")
+        print("        cmdclass=versioneer.get_cmdclass(),  ...)")
+        print("")
+        errors += 1
+    if setters:
+        print("You should remove lines like 'versioneer.VCS = ' and")
+        print("'versioneer.versionfile_source = ' . This configuration")
+        print("now lives in setup.cfg, and should be removed from setup.py")
+        print("")
+        errors += 1
+    return errors
+
+
+if __name__ == "__main__":
+    cmd = sys.argv[1]
+    if cmd == "setup":
+        errors = do_setup()
+        errors += scan_setup_py()
+        if errors:
+            sys.exit(1)



View it on GitLab: https://salsa.debian.org/debian-gis-team/pyorbital/compare/933deab3f6f8653e5119082254d2353806eea666...187148512d1ee7c5df514ee603f308d5cfcfbe4c

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyorbital/compare/933deab3f6f8653e5119082254d2353806eea666...187148512d1ee7c5df514ee603f308d5cfcfbe4c
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20181111/01ee6f8f/attachment-0001.html>


More information about the Pkg-grass-devel mailing list