1# Version: 0.19
2
3"""The Versioneer - like a rocketeer, but for versions.
4
5The Versioneer
6==============
7
8* like a rocketeer, but for versions!
9* https://github.com/python-versioneer/python-versioneer
10* Brian Warner
11* License: Public Domain
12* Compatible with: Python 3.6, 3.7, 3.8, 3.9 and pypy3
13* [![Latest Version][pypi-image]][pypi-url]
14* [![Build Status][travis-image]][travis-url]
15
16This is a tool for managing a recorded version number in distutils-based
17python projects. The goal is to remove the tedious and error-prone "update
18the embedded version string" step from your release process. Making a new
19release should be as easy as recording a new tag in your version-control
20system, and maybe making new tarballs.
21
22
23## Quick Install
24
25* `pip install versioneer` to somewhere in your $PATH
26* add a `[versioneer]` section to your setup.cfg (see [Install](INSTALL.md))
27* run `versioneer install` in your source tree, commit the results
28* Verify version information with `python setup.py version`
29
30## Version Identifiers
31
32Source trees come from a variety of places:
33
34* a version-control system checkout (mostly used by developers)
35* a nightly tarball, produced by build automation
36* a snapshot tarball, produced by a web-based VCS browser, like github's
37  "tarball from tag" feature
38* a release tarball, produced by "setup.py sdist", distributed through PyPI
39
40Within each source tree, the version identifier (either a string or a number,
41this tool is format-agnostic) can come from a variety of places:
42
43* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows
44  about recent "tags" and an absolute revision-id
45* the name of the directory into which the tarball was unpacked
46* an expanded VCS keyword ($Id$, etc)
47* a `_version.py` created by some earlier build step
48
49For released software, the version identifier is closely related to a VCS
50tag. Some projects use tag names that include more than just the version
51string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool
52needs to strip the tag prefix to extract the version identifier. For
53unreleased software (between tags), the version identifier should provide
54enough information to help developers recreate the same tree, while also
55giving them an idea of roughly how old the tree is (after version 1.2, before
56version 1.3). Many VCS systems can report a description that captures this,
57for example `git describe --tags --dirty --always` reports things like
58"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the
590.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has
60uncommitted changes).
61
62The version identifier is used for multiple purposes:
63
64* to allow the module to self-identify its version: `myproject.__version__`
65* to choose a name and prefix for a 'setup.py sdist' tarball
66
67## Theory of Operation
68
69Versioneer works by adding a special `_version.py` file into your source
70tree, where your `__init__.py` can import it. This `_version.py` knows how to
71dynamically ask the VCS tool for version information at import time.
72
73`_version.py` also contains `$Revision$` markers, and the installation
74process marks `_version.py` to have this marker rewritten with a tag name
75during the `git archive` command. As a result, generated tarballs will
76contain enough information to get the proper version.
77
78To allow `setup.py` to compute a version too, a `versioneer.py` is added to
79the top level of your source tree, next to `setup.py` and the `setup.cfg`
80that configures it. This overrides several distutils/setuptools commands to
81compute the version when invoked, and changes `setup.py build` and `setup.py
82sdist` to replace `_version.py` with a small static file that contains just
83the generated version data.
84
85## Installation
86
87See [INSTALL.md](./INSTALL.md) for detailed installation instructions.
88
89## Version-String Flavors
90
91Code which uses Versioneer can learn about its version string at runtime by
92importing `_version` from your main `__init__.py` file and running the
93`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can
94import the top-level `versioneer.py` and run `get_versions()`.
95
96Both functions return a dictionary with different flavors of version
97information:
98
99* `['version']`: A condensed version string, rendered using the selected
100  style. This is the most commonly used value for the project's version
101  string. The default "pep440" style yields strings like `0.11`,
102  `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section
103  below for alternative styles.
104
105* `['full-revisionid']`: detailed revision identifier. For Git, this is the
106  full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac".
107
108* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the
109  commit date in ISO 8601 format. This will be None if the date is not
110  available.
111
112* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that
113  this is only accurate if run in a VCS checkout, otherwise it is likely to
114  be False or None
115
116* `['error']`: if the version string could not be computed, this will be set
117  to a string describing the problem, otherwise it will be None. It may be
118  useful to throw an exception in setup.py if this is set, to avoid e.g.
119  creating tarballs with a version string of "unknown".
120
121Some variants are more useful than others. Including `full-revisionid` in a
122bug report should allow developers to reconstruct the exact code being tested
123(or indicate the presence of local changes that should be shared with the
124developers). `version` is suitable for display in an "about" box or a CLI
125`--version` output: it can be easily compared against release notes and lists
126of bugs fixed in various releases.
127
128The installer adds the following text to your `__init__.py` to place a basic
129version in `YOURPROJECT.__version__`:
130
131    from ._version import get_versions
132    __version__ = get_versions()['version']
133    del get_versions
134
135## Styles
136
137The setup.cfg `style=` configuration controls how the VCS information is
138rendered into a version string.
139
140The default style, "pep440", produces a PEP440-compliant string, equal to the
141un-prefixed tag name for actual releases, and containing an additional "local
142version" section with more detail for in-between builds. For Git, this is
143TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags
144--dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the
145tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and
146that this commit is two revisions ("+2") beyond the "0.11" tag. For released
147software (exactly equal to a known tag), the identifier will only contain the
148stripped tag, e.g. "0.11".
149
150Other styles are available. See [details.md](details.md) in the Versioneer
151source tree for descriptions.
152
153## Debugging
154
155Versioneer tries to avoid fatal errors: if something goes wrong, it will tend
156to return a version of "0+unknown". To investigate the problem, run `setup.py
157version`, which will run the version-lookup code in a verbose mode, and will
158display the full contents of `get_versions()` (including the `error` string,
159which may help identify what went wrong).
160
161## Known Limitations
162
163Some situations are known to cause problems for Versioneer. This details the
164most significant ones. More can be found on Github
165[issues page](https://github.com/python-versioneer/python-versioneer/issues).
166
167### Subprojects
168
169Versioneer has limited support for source trees in which `setup.py` is not in
170the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are
171two common reasons why `setup.py` might not be in the root:
172
173* Source trees which contain multiple subprojects, such as
174  [Buildbot](https://github.com/buildbot/buildbot), which contains both
175  "master" and "slave" subprojects, each with their own `setup.py`,
176  `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI
177  distributions (and upload multiple independently-installable tarballs).
178* Source trees whose main purpose is to contain a C library, but which also
179  provide bindings to Python (and perhaps other languages) in subdirectories.
180
181Versioneer will look for `.git` in parent directories, and most operations
182should get the right version string. However `pip` and `setuptools` have bugs
183and implementation details which frequently cause `pip install .` from a
184subproject directory to fail to find a correct version string (so it usually
185defaults to `0+unknown`).
186
187`pip install --editable .` should work correctly. `setup.py install` might
188work too.
189
190Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in
191some later version.
192
193[Bug #38](https://github.com/python-versioneer/python-versioneer/issues/38) is tracking
194this issue. The discussion in
195[PR #61](https://github.com/python-versioneer/python-versioneer/pull/61) describes the
196issue from the Versioneer side in more detail.
197[pip PR#3176](https://github.com/pypa/pip/pull/3176) and
198[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve
199pip to let Versioneer work correctly.
200
201Versioneer-0.16 and earlier only looked for a `.git` directory next to the
202`setup.cfg`, so subprojects were completely unsupported with those releases.
203
204### Editable installs with setuptools <= 18.5
205
206`setup.py develop` and `pip install --editable .` allow you to install a
207project into a virtualenv once, then continue editing the source code (and
208test) without re-installing after every change.
209
210"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a
211convenient way to specify executable scripts that should be installed along
212with the python package.
213
214These both work as expected when using modern setuptools. When using
215setuptools-18.5 or earlier, however, certain operations will cause
216`pkg_resources.DistributionNotFound` errors when running the entrypoint
217script, which must be resolved by re-installing the package. This happens
218when the install happens with one version, then the egg_info data is
219regenerated while a different version is checked out. Many setup.py commands
220cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into
221a different virtualenv), so this can be surprising.
222
223[Bug #83](https://github.com/python-versioneer/python-versioneer/issues/83) describes
224this one, but upgrading to a newer version of setuptools should probably
225resolve it.
226
227
228## Updating Versioneer
229
230To upgrade your project to a new release of Versioneer, do the following:
231
232* install the new Versioneer (`pip install -U versioneer` or equivalent)
233* edit `setup.cfg`, if necessary, to include any new configuration settings
234  indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details.
235* re-run `versioneer install` in your source tree, to replace
236  `SRC/_version.py`
237* commit any changed files
238
239## Future Directions
240
241This tool is designed to make it easily extended to other version-control
242systems: all VCS-specific components are in separate directories like
243src/git/ . The top-level `versioneer.py` script is assembled from these
244components by running make-versioneer.py . In the future, make-versioneer.py
245will take a VCS name as an argument, and will construct a version of
246`versioneer.py` that is specific to the given VCS. It might also take the
247configuration arguments that are currently provided manually during
248installation by editing setup.py . Alternatively, it might go the other
249direction and include code from all supported VCS systems, reducing the
250number of intermediate scripts.
251
252## Similar projects
253
254* [setuptools_scm](https://github.com/pypa/setuptools_scm/) - a non-vendored build-time
255  dependency
256* [minver](https://github.com/jbweston/miniver) - a lightweight reimplementation of
257  versioneer
258
259## License
260
261To make Versioneer easier to embed, all its code is dedicated to the public
262domain. The `_version.py` that it creates is also in the public domain.
263Specifically, both are released under the Creative Commons "Public Domain
264Dedication" license (CC0-1.0), as described in
265https://creativecommons.org/publicdomain/zero/1.0/ .
266
267[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg
268[pypi-url]: https://pypi.python.org/pypi/versioneer/
269[travis-image]:
270https://img.shields.io/travis/com/python-versioneer/python-versioneer.svg
271[travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer
272
273"""
274
275import configparser
276import errno
277import json
278import os
279import re
280import subprocess
281import sys
282
283
284class VersioneerConfig:
285    """Container for Versioneer configuration parameters."""
286
287
288def get_root():
289    """Get the project root directory.
290
291    We require that all commands are run from the project root, i.e. the
292    directory that contains setup.py, setup.cfg, and versioneer.py .
293    """
294    root = os.path.realpath(os.path.abspath(os.getcwd()))
295    setup_py = os.path.join(root, "setup.py")
296    versioneer_py = os.path.join(root, "versioneer.py")
297    if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)):
298        # allow 'python path/to/setup.py COMMAND'
299        root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0])))
300        setup_py = os.path.join(root, "setup.py")
301        versioneer_py = os.path.join(root, "versioneer.py")
302    if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)):
303        err = (
304            "Versioneer was unable to run the project root directory. "
305            "Versioneer requires setup.py to be executed from "
306            "its immediate directory (like 'python setup.py COMMAND'), "
307            "or in a way that lets it use sys.argv[0] to find the root "
308            "(like 'python path/to/setup.py COMMAND')."
309        )
310        raise VersioneerBadRootError(err)
311    try:
312        # Certain runtime workflows (setup.py install/develop in a setuptools
313        # tree) execute all dependencies in a single python process, so
314        # "versioneer" may be imported multiple times, and python's shared
315        # module-import table will cache the first one. So we can't use
316        # os.path.dirname(__file__), as that will find whichever
317        # versioneer.py was first imported, even in later projects.
318        me = os.path.realpath(os.path.abspath(__file__))
319        me_dir = os.path.normcase(os.path.splitext(me)[0])
320        vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0])
321        if me_dir != vsr_dir:
322            print(
323                "Warning: build in %s is using versioneer.py from %s"
324                % (os.path.dirname(me), versioneer_py)
325            )
326    except NameError:
327        pass
328    return root
329
330
331def get_config_from_root(root):
332    """Read the project setup.cfg file to determine Versioneer config."""
333    # This might raise EnvironmentError (if setup.cfg is missing), or
334    # configparser.NoSectionError (if it lacks a [versioneer] section), or
335    # configparser.NoOptionError (if it lacks "VCS="). See the docstring at
336    # the top of versioneer.py for instructions on writing your setup.cfg .
337    setup_cfg = os.path.join(root, "setup.cfg")
338    parser = configparser.ConfigParser()
339    with open(setup_cfg, "r") as f:
340        parser.read_file(f)
341    VCS = parser.get("versioneer", "VCS")  # mandatory
342
343    def get(parser, name):
344        if parser.has_option("versioneer", name):
345            return parser.get("versioneer", name)
346        return None
347
348    cfg = VersioneerConfig()
349    cfg.VCS = VCS
350    cfg.style = get(parser, "style") or ""
351    cfg.versionfile_source = get(parser, "versionfile_source")
352    cfg.versionfile_build = get(parser, "versionfile_build")
353    cfg.tag_prefix = get(parser, "tag_prefix")
354    if cfg.tag_prefix in ("''", '""'):
355        cfg.tag_prefix = ""
356    cfg.parentdir_prefix = get(parser, "parentdir_prefix")
357    cfg.verbose = get(parser, "verbose")
358    return cfg
359
360
361class NotThisMethod(Exception):
362    """Exception raised if a method is not valid for the current scenario."""
363
364
365# these dictionaries contain VCS-specific tools
366LONG_VERSION_PY = {}
367HANDLERS = {}
368
369
370def register_vcs_handler(vcs, method):  # decorator
371    """Create decorator to mark a method as the handler of a VCS."""
372
373    def decorate(f):
374        """Store f in HANDLERS[vcs][method]."""
375        if vcs not in HANDLERS:
376            HANDLERS[vcs] = {}
377        HANDLERS[vcs][method] = f
378        return f
379
380    return decorate
381
382
383def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None):
384    """Call the given command(s)."""
385    assert isinstance(commands, list)
386    p = None
387    for c in commands:
388        try:
389            dispcmd = str([c] + args)
390            # remember shell=False, so use git.cmd on windows, not just git
391            p = subprocess.Popen(
392                [c] + args,
393                cwd=cwd,
394                env=env,
395                stdout=subprocess.PIPE,
396                stderr=(subprocess.PIPE if hide_stderr else None),
397            )
398            break
399        except EnvironmentError:
400            e = sys.exc_info()[1]
401            if e.errno == errno.ENOENT:
402                continue
403            if verbose:
404                print("unable to run %s" % dispcmd)
405                print(e)
406            return None, None
407    else:
408        if verbose:
409            print("unable to find command, tried %s" % (commands,))
410        return None, None
411    stdout = p.communicate()[0].strip().decode()
412    if p.returncode != 0:
413        if verbose:
414            print("unable to run %s (error)" % dispcmd)
415            print("stdout was %s" % stdout)
416        return None, p.returncode
417    return stdout, p.returncode
418
419
420LONG_VERSION_PY[
421    "git"
422] = r'''
423# This file helps to compute a version number in source trees obtained from
424# git-archive tarball (such as those provided by githubs download-from-tag
425# feature). Distribution tarballs (built by setup.py sdist) and build
426# directories (produced by setup.py build) will contain a much shorter file
427# that just contains the computed version number.
428
429# This file is released into the public domain. Generated by
430# versioneer-0.19 (https://github.com/python-versioneer/python-versioneer)
431
432"""Git implementation of _version.py."""
433
434import errno
435import os
436import re
437import subprocess
438import sys
439
440
441def get_keywords():
442    """Get the keywords needed to look up the version information."""
443    # these strings will be replaced by git during git-archive.
444    # setup.py/versioneer.py will grep for the variable names, so they must
445    # each be defined on a line of their own. _version.py will just call
446    # get_keywords().
447    git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s"
448    git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s"
449    git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s"
450    keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
451    return keywords
452
453
454class VersioneerConfig:
455    """Container for Versioneer configuration parameters."""
456
457
458def get_config():
459    """Create, populate and return the VersioneerConfig() object."""
460    # these strings are filled in when 'setup.py versioneer' creates
461    # _version.py
462    cfg = VersioneerConfig()
463    cfg.VCS = "git"
464    cfg.style = "%(STYLE)s"
465    cfg.tag_prefix = "%(TAG_PREFIX)s"
466    cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s"
467    cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s"
468    cfg.verbose = False
469    return cfg
470
471
472class NotThisMethod(Exception):
473    """Exception raised if a method is not valid for the current scenario."""
474
475
476LONG_VERSION_PY = {}
477HANDLERS = {}
478
479
480def register_vcs_handler(vcs, method):  # decorator
481    """Create decorator to mark a method as the handler of a VCS."""
482    def decorate(f):
483        """Store f in HANDLERS[vcs][method]."""
484        if vcs not in HANDLERS:
485            HANDLERS[vcs] = {}
486        HANDLERS[vcs][method] = f
487        return f
488    return decorate
489
490
491def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
492                env=None):
493    """Call the given command(s)."""
494    assert isinstance(commands, list)
495    p = None
496    for c in commands:
497        try:
498            dispcmd = str([c] + args)
499            # remember shell=False, so use git.cmd on windows, not just git
500            p = subprocess.Popen([c] + args, cwd=cwd, env=env,
501                                 stdout=subprocess.PIPE,
502                                 stderr=(subprocess.PIPE if hide_stderr
503                                         else None))
504            break
505        except EnvironmentError:
506            e = sys.exc_info()[1]
507            if e.errno == errno.ENOENT:
508                continue
509            if verbose:
510                print("unable to run %%s" %% dispcmd)
511                print(e)
512            return None, None
513    else:
514        if verbose:
515            print("unable to find command, tried %%s" %% (commands,))
516        return None, None
517    stdout = p.communicate()[0].strip().decode()
518    if p.returncode != 0:
519        if verbose:
520            print("unable to run %%s (error)" %% dispcmd)
521            print("stdout was %%s" %% stdout)
522        return None, p.returncode
523    return stdout, p.returncode
524
525
526def versions_from_parentdir(parentdir_prefix, root, verbose):
527    """Try to determine the version from the parent directory name.
528
529    Source tarballs conventionally unpack into a directory that includes both
530    the project name and a version string. We will also support searching up
531    two directory levels for an appropriately named parent directory
532    """
533    rootdirs = []
534
535    for i in range(3):
536        dirname = os.path.basename(root)
537        if dirname.startswith(parentdir_prefix):
538            return {"version": dirname[len(parentdir_prefix):],
539                    "full-revisionid": None,
540                    "dirty": False, "error": None, "date": None}
541        else:
542            rootdirs.append(root)
543            root = os.path.dirname(root)  # up a level
544
545    if verbose:
546        print("Tried directories %%s but none started with prefix %%s" %%
547              (str(rootdirs), parentdir_prefix))
548    raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
549
550
551@register_vcs_handler("git", "get_keywords")
552def git_get_keywords(versionfile_abs):
553    """Extract version information from the given file."""
554    # the code embedded in _version.py can just fetch the value of these
555    # keywords. When used from setup.py, we don't want to import _version.py,
556    # so we do it with a regexp instead. This function is not used from
557    # _version.py.
558    keywords = {}
559    try:
560        f = open(versionfile_abs, "r")
561        for line in f.readlines():
562            if line.strip().startswith("git_refnames ="):
563                mo = re.search(r'=\s*"(.*)"', line)
564                if mo:
565                    keywords["refnames"] = mo.group(1)
566            if line.strip().startswith("git_full ="):
567                mo = re.search(r'=\s*"(.*)"', line)
568                if mo:
569                    keywords["full"] = mo.group(1)
570            if line.strip().startswith("git_date ="):
571                mo = re.search(r'=\s*"(.*)"', line)
572                if mo:
573                    keywords["date"] = mo.group(1)
574        f.close()
575    except EnvironmentError:
576        pass
577    return keywords
578
579
580@register_vcs_handler("git", "keywords")
581def git_versions_from_keywords(keywords, tag_prefix, verbose):
582    """Get version information from git keywords."""
583    if not keywords:
584        raise NotThisMethod("no keywords at all, weird")
585    date = keywords.get("date")
586    if date is not None:
587        # Use only the last line.  Previous lines may contain GPG signature
588        # information.
589        date = date.splitlines()[-1]
590
591        # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant
592        # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601
593        # -like" string, which we must then edit to make compliant), because
594        # it's been around since git-1.5.3, and it's too difficult to
595        # discover which version we're using, or to work around using an
596        # older one.
597        date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
598    refnames = keywords["refnames"].strip()
599    if refnames.startswith("$Format"):
600        if verbose:
601            print("keywords are unexpanded, not using")
602        raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
603    refs = set([r.strip() for r in refnames.strip("()").split(",")])
604    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
605    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
606    TAG = "tag: "
607    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
608    if not tags:
609        # Either we're using git < 1.8.3, or there really are no tags. We use
610        # a heuristic: assume all version tags have a digit. The old git %%d
611        # expansion behaves like git log --decorate=short and strips out the
612        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
613        # between branches and tags. By ignoring refnames without digits, we
614        # filter out many common branch names like "release" and
615        # "stabilization", as well as "HEAD" and "master".
616        tags = set([r for r in refs if re.search(r'\d', r)])
617        if verbose:
618            print("discarding '%%s', no digits" %% ",".join(refs - tags))
619    if verbose:
620        print("likely tags: %%s" %% ",".join(sorted(tags)))
621    for ref in sorted(tags):
622        # sorting will prefer e.g. "2.0" over "2.0rc1"
623        if ref.startswith(tag_prefix):
624            r = ref[len(tag_prefix):]
625            if verbose:
626                print("picking %%s" %% r)
627            return {"version": r,
628                    "full-revisionid": keywords["full"].strip(),
629                    "dirty": False, "error": None,
630                    "date": date}
631    # no suitable tags, so version is "0+unknown", but full hex is still there
632    if verbose:
633        print("no suitable tags, using unknown + full revision id")
634    return {"version": "0+unknown",
635            "full-revisionid": keywords["full"].strip(),
636            "dirty": False, "error": "no suitable tags", "date": None}
637
638
639@register_vcs_handler("git", "pieces_from_vcs")
640def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
641    """Get version from 'git describe' in the root of the source tree.
642
643    This only gets called if the git-archive 'subst' keywords were *not*
644    expanded, and _version.py hasn't already been rewritten with a short
645    version string, meaning we're inside a checked out source tree.
646    """
647    GITS = ["git"]
648    if sys.platform == "win32":
649        GITS = ["git.cmd", "git.exe"]
650
651    out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
652                          hide_stderr=True)
653    if rc != 0:
654        if verbose:
655            print("Directory %%s not under git control" %% root)
656        raise NotThisMethod("'git rev-parse --git-dir' returned error")
657
658    # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
659    # if there isn't one, this yields HEX[-dirty] (no NUM)
660    describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
661                                          "--always", "--long",
662                                          "--match", "%%s*" %% tag_prefix],
663                                   cwd=root)
664    # --long was added in git-1.5.5
665    if describe_out is None:
666        raise NotThisMethod("'git describe' failed")
667    describe_out = describe_out.strip()
668    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
669    if full_out is None:
670        raise NotThisMethod("'git rev-parse' failed")
671    full_out = full_out.strip()
672
673    pieces = {}
674    pieces["long"] = full_out
675    pieces["short"] = full_out[:7]  # maybe improved later
676    pieces["error"] = None
677
678    # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
679    # TAG might have hyphens.
680    git_describe = describe_out
681
682    # look for -dirty suffix
683    dirty = git_describe.endswith("-dirty")
684    pieces["dirty"] = dirty
685    if dirty:
686        git_describe = git_describe[:git_describe.rindex("-dirty")]
687
688    # now we have TAG-NUM-gHEX or HEX
689
690    if "-" in git_describe:
691        # TAG-NUM-gHEX
692        mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
693        if not mo:
694            # unparseable. Maybe git-describe is misbehaving?
695            pieces["error"] = ("unable to parse git-describe output: '%%s'"
696                               %% describe_out)
697            return pieces
698
699        # tag
700        full_tag = mo.group(1)
701        if not full_tag.startswith(tag_prefix):
702            if verbose:
703                fmt = "tag '%%s' doesn't start with prefix '%%s'"
704                print(fmt %% (full_tag, tag_prefix))
705            pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'"
706                               %% (full_tag, tag_prefix))
707            return pieces
708        pieces["closest-tag"] = full_tag[len(tag_prefix):]
709
710        # distance: number of commits since tag
711        pieces["distance"] = int(mo.group(2))
712
713        # commit: short hex revision ID
714        pieces["short"] = mo.group(3)
715
716    else:
717        # HEX: no tags
718        pieces["closest-tag"] = None
719        count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
720                                    cwd=root)
721        pieces["distance"] = int(count_out)  # total number of commits
722
723    # commit date: see ISO-8601 comment in git_versions_from_keywords()
724    date = run_command(GITS, ["show", "-s", "--format=%%ci", "HEAD"],
725                       cwd=root)[0].strip()
726    # Use only the last line.  Previous lines may contain GPG signature
727    # information.
728    date = date.splitlines()[-1]
729    pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
730
731    return pieces
732
733
734def plus_or_dot(pieces):
735    """Return a + if we don't already have one, else return a ."""
736    if "+" in pieces.get("closest-tag", ""):
737        return "."
738    return "+"
739
740
741def render_pep440(pieces):
742    """Build up version string, with post-release "local version identifier".
743
744    Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
745    get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
746
747    Exceptions:
748    1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
749    """
750    if pieces["closest-tag"]:
751        rendered = pieces["closest-tag"]
752        if pieces["distance"] or pieces["dirty"]:
753            rendered += plus_or_dot(pieces)
754            rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"])
755            if pieces["dirty"]:
756                rendered += ".dirty"
757    else:
758        # exception #1
759        rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"],
760                                          pieces["short"])
761        if pieces["dirty"]:
762            rendered += ".dirty"
763    return rendered
764
765
766def render_pep440_pre(pieces):
767    """TAG[.post0.devDISTANCE] -- No -dirty.
768
769    Exceptions:
770    1: no tags. 0.post0.devDISTANCE
771    """
772    if pieces["closest-tag"]:
773        rendered = pieces["closest-tag"]
774        if pieces["distance"]:
775            rendered += ".post0.dev%%d" %% pieces["distance"]
776    else:
777        # exception #1
778        rendered = "0.post0.dev%%d" %% pieces["distance"]
779    return rendered
780
781
782def render_pep440_post(pieces):
783    """TAG[.postDISTANCE[.dev0]+gHEX] .
784
785    The ".dev0" means dirty. Note that .dev0 sorts backwards
786    (a dirty tree will appear "older" than the corresponding clean one),
787    but you shouldn't be releasing software with -dirty anyways.
788
789    Exceptions:
790    1: no tags. 0.postDISTANCE[.dev0]
791    """
792    if pieces["closest-tag"]:
793        rendered = pieces["closest-tag"]
794        if pieces["distance"] or pieces["dirty"]:
795            rendered += ".post%%d" %% pieces["distance"]
796            if pieces["dirty"]:
797                rendered += ".dev0"
798            rendered += plus_or_dot(pieces)
799            rendered += "g%%s" %% pieces["short"]
800    else:
801        # exception #1
802        rendered = "0.post%%d" %% pieces["distance"]
803        if pieces["dirty"]:
804            rendered += ".dev0"
805        rendered += "+g%%s" %% pieces["short"]
806    return rendered
807
808
809def render_pep440_old(pieces):
810    """TAG[.postDISTANCE[.dev0]] .
811
812    The ".dev0" means dirty.
813
814    Exceptions:
815    1: no tags. 0.postDISTANCE[.dev0]
816    """
817    if pieces["closest-tag"]:
818        rendered = pieces["closest-tag"]
819        if pieces["distance"] or pieces["dirty"]:
820            rendered += ".post%%d" %% pieces["distance"]
821            if pieces["dirty"]:
822                rendered += ".dev0"
823    else:
824        # exception #1
825        rendered = "0.post%%d" %% pieces["distance"]
826        if pieces["dirty"]:
827            rendered += ".dev0"
828    return rendered
829
830
831def render_git_describe(pieces):
832    """TAG[-DISTANCE-gHEX][-dirty].
833
834    Like 'git describe --tags --dirty --always'.
835
836    Exceptions:
837    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
838    """
839    if pieces["closest-tag"]:
840        rendered = pieces["closest-tag"]
841        if pieces["distance"]:
842            rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
843    else:
844        # exception #1
845        rendered = pieces["short"]
846    if pieces["dirty"]:
847        rendered += "-dirty"
848    return rendered
849
850
851def render_git_describe_long(pieces):
852    """TAG-DISTANCE-gHEX[-dirty].
853
854    Like 'git describe --tags --dirty --always -long'.
855    The distance/hash is unconditional.
856
857    Exceptions:
858    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
859    """
860    if pieces["closest-tag"]:
861        rendered = pieces["closest-tag"]
862        rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
863    else:
864        # exception #1
865        rendered = pieces["short"]
866    if pieces["dirty"]:
867        rendered += "-dirty"
868    return rendered
869
870
871def render(pieces, style):
872    """Render the given version pieces into the requested style."""
873    if pieces["error"]:
874        return {"version": "unknown",
875                "full-revisionid": pieces.get("long"),
876                "dirty": None,
877                "error": pieces["error"],
878                "date": None}
879
880    if not style or style == "default":
881        style = "pep440"  # the default
882
883    if style == "pep440":
884        rendered = render_pep440(pieces)
885    elif style == "pep440-pre":
886        rendered = render_pep440_pre(pieces)
887    elif style == "pep440-post":
888        rendered = render_pep440_post(pieces)
889    elif style == "pep440-old":
890        rendered = render_pep440_old(pieces)
891    elif style == "git-describe":
892        rendered = render_git_describe(pieces)
893    elif style == "git-describe-long":
894        rendered = render_git_describe_long(pieces)
895    else:
896        raise ValueError("unknown style '%%s'" %% style)
897
898    return {"version": rendered, "full-revisionid": pieces["long"],
899            "dirty": pieces["dirty"], "error": None,
900            "date": pieces.get("date")}
901
902
903def get_versions():
904    """Get version information or return default if unable to do so."""
905    # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
906    # __file__, we can work backwards from there to the root. Some
907    # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
908    # case we can only use expanded keywords.
909
910    cfg = get_config()
911    verbose = cfg.verbose
912
913    try:
914        return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
915                                          verbose)
916    except NotThisMethod:
917        pass
918
919    try:
920        root = os.path.realpath(__file__)
921        # versionfile_source is the relative path from the top of the source
922        # tree (where the .git directory might live) to this file. Invert
923        # this to find the root from __file__.
924        for i in cfg.versionfile_source.split('/'):
925            root = os.path.dirname(root)
926    except NameError:
927        return {"version": "0+unknown", "full-revisionid": None,
928                "dirty": None,
929                "error": "unable to find root of source tree",
930                "date": None}
931
932    try:
933        pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
934        return render(pieces, cfg.style)
935    except NotThisMethod:
936        pass
937
938    try:
939        if cfg.parentdir_prefix:
940            return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
941    except NotThisMethod:
942        pass
943
944    return {"version": "0+unknown", "full-revisionid": None,
945            "dirty": None,
946            "error": "unable to compute version", "date": None}
947'''
948
949
950@register_vcs_handler("git", "get_keywords")
951def git_get_keywords(versionfile_abs):
952    """Extract version information from the given file."""
953    # the code embedded in _version.py can just fetch the value of these
954    # keywords. When used from setup.py, we don't want to import _version.py,
955    # so we do it with a regexp instead. This function is not used from
956    # _version.py.
957    keywords = {}
958    try:
959        f = open(versionfile_abs, "r")
960        for line in f.readlines():
961            if line.strip().startswith("git_refnames ="):
962                mo = re.search(r'=\s*"(.*)"', line)
963                if mo:
964                    keywords["refnames"] = mo.group(1)
965            if line.strip().startswith("git_full ="):
966                mo = re.search(r'=\s*"(.*)"', line)
967                if mo:
968                    keywords["full"] = mo.group(1)
969            if line.strip().startswith("git_date ="):
970                mo = re.search(r'=\s*"(.*)"', line)
971                if mo:
972                    keywords["date"] = mo.group(1)
973        f.close()
974    except EnvironmentError:
975        pass
976    return keywords
977
978
979@register_vcs_handler("git", "keywords")
980def git_versions_from_keywords(keywords, tag_prefix, verbose):
981    """Get version information from git keywords."""
982    if not keywords:
983        raise NotThisMethod("no keywords at all, weird")
984    date = keywords.get("date")
985    if date is not None:
986        # Use only the last line.  Previous lines may contain GPG signature
987        # information.
988        date = date.splitlines()[-1]
989
990        # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
991        # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
992        # -like" string, which we must then edit to make compliant), because
993        # it's been around since git-1.5.3, and it's too difficult to
994        # discover which version we're using, or to work around using an
995        # older one.
996        date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
997    refnames = keywords["refnames"].strip()
998    if refnames.startswith("$Format"):
999        if verbose:
1000            print("keywords are unexpanded, not using")
1001        raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
1002    refs = set([r.strip() for r in refnames.strip("()").split(",")])
1003    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
1004    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
1005    TAG = "tag: "
1006    tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)])
1007    if not tags:
1008        # Either we're using git < 1.8.3, or there really are no tags. We use
1009        # a heuristic: assume all version tags have a digit. The old git %d
1010        # expansion behaves like git log --decorate=short and strips out the
1011        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
1012        # between branches and tags. By ignoring refnames without digits, we
1013        # filter out many common branch names like "release" and
1014        # "stabilization", as well as "HEAD" and "master".
1015        tags = set([r for r in refs if re.search(r"\d", r)])
1016        if verbose:
1017            print("discarding '%s', no digits" % ",".join(refs - tags))
1018    if verbose:
1019        print("likely tags: %s" % ",".join(sorted(tags)))
1020    for ref in sorted(tags):
1021        # sorting will prefer e.g. "2.0" over "2.0rc1"
1022        if ref.startswith(tag_prefix):
1023            r = ref[len(tag_prefix) :]
1024            if verbose:
1025                print("picking %s" % r)
1026            return {
1027                "version": r,
1028                "full-revisionid": keywords["full"].strip(),
1029                "dirty": False,
1030                "error": None,
1031                "date": date,
1032            }
1033    # no suitable tags, so version is "0+unknown", but full hex is still there
1034    if verbose:
1035        print("no suitable tags, using unknown + full revision id")
1036    return {
1037        "version": "0+unknown",
1038        "full-revisionid": keywords["full"].strip(),
1039        "dirty": False,
1040        "error": "no suitable tags",
1041        "date": None,
1042    }
1043
1044
1045@register_vcs_handler("git", "pieces_from_vcs")
1046def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
1047    """Get version from 'git describe' in the root of the source tree.
1048
1049    This only gets called if the git-archive 'subst' keywords were *not*
1050    expanded, and _version.py hasn't already been rewritten with a short
1051    version string, meaning we're inside a checked out source tree.
1052    """
1053    GITS = ["git"]
1054    if sys.platform == "win32":
1055        GITS = ["git.cmd", "git.exe"]
1056
1057    out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True)
1058    if rc != 0:
1059        if verbose:
1060            print("Directory %s not under git control" % root)
1061        raise NotThisMethod("'git rev-parse --git-dir' returned error")
1062
1063    # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
1064    # if there isn't one, this yields HEX[-dirty] (no NUM)
1065    describe_out, rc = run_command(
1066        GITS,
1067        [
1068            "describe",
1069            "--tags",
1070            "--dirty",
1071            "--always",
1072            "--long",
1073            "--match",
1074            "%s*" % tag_prefix,
1075        ],
1076        cwd=root,
1077    )
1078    # --long was added in git-1.5.5
1079    if describe_out is None:
1080        raise NotThisMethod("'git describe' failed")
1081    describe_out = describe_out.strip()
1082    full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
1083    if full_out is None:
1084        raise NotThisMethod("'git rev-parse' failed")
1085    full_out = full_out.strip()
1086
1087    pieces = {}
1088    pieces["long"] = full_out
1089    pieces["short"] = full_out[:7]  # maybe improved later
1090    pieces["error"] = None
1091
1092    # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
1093    # TAG might have hyphens.
1094    git_describe = describe_out
1095
1096    # look for -dirty suffix
1097    dirty = git_describe.endswith("-dirty")
1098    pieces["dirty"] = dirty
1099    if dirty:
1100        git_describe = git_describe[: git_describe.rindex("-dirty")]
1101
1102    # now we have TAG-NUM-gHEX or HEX
1103
1104    if "-" in git_describe:
1105        # TAG-NUM-gHEX
1106        mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe)
1107        if not mo:
1108            # unparseable. Maybe git-describe is misbehaving?
1109            pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out
1110            return pieces
1111
1112        # tag
1113        full_tag = mo.group(1)
1114        if not full_tag.startswith(tag_prefix):
1115            if verbose:
1116                fmt = "tag '%s' doesn't start with prefix '%s'"
1117                print(fmt % (full_tag, tag_prefix))
1118            pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (
1119                full_tag,
1120                tag_prefix,
1121            )
1122            return pieces
1123        pieces["closest-tag"] = full_tag[len(tag_prefix) :]
1124
1125        # distance: number of commits since tag
1126        pieces["distance"] = int(mo.group(2))
1127
1128        # commit: short hex revision ID
1129        pieces["short"] = mo.group(3)
1130
1131    else:
1132        # HEX: no tags
1133        pieces["closest-tag"] = None
1134        count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
1135        pieces["distance"] = int(count_out)  # total number of commits
1136
1137    # commit date: see ISO-8601 comment in git_versions_from_keywords()
1138    date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[
1139        0
1140    ].strip()
1141    # Use only the last line.  Previous lines may contain GPG signature
1142    # information.
1143    date = date.splitlines()[-1]
1144    pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
1145
1146    return pieces
1147
1148
1149def do_vcs_install(manifest_in, versionfile_source, ipy):
1150    """Git-specific installation logic for Versioneer.
1151
1152    For Git, this means creating/changing .gitattributes to mark _version.py
1153    for export-subst keyword substitution.
1154    """
1155    GITS = ["git"]
1156    if sys.platform == "win32":
1157        GITS = ["git.cmd", "git.exe"]
1158    files = [manifest_in, versionfile_source]
1159    if ipy:
1160        files.append(ipy)
1161    try:
1162        me = __file__
1163        if me.endswith(".pyc") or me.endswith(".pyo"):
1164            me = os.path.splitext(me)[0] + ".py"
1165        versioneer_file = os.path.relpath(me)
1166    except NameError:
1167        versioneer_file = "versioneer.py"
1168    files.append(versioneer_file)
1169    present = False
1170    try:
1171        f = open(".gitattributes", "r")
1172        for line in f.readlines():
1173            if line.strip().startswith(versionfile_source):
1174                if "export-subst" in line.strip().split()[1:]:
1175                    present = True
1176        f.close()
1177    except EnvironmentError:
1178        pass
1179    if not present:
1180        f = open(".gitattributes", "a+")
1181        f.write("%s export-subst\n" % versionfile_source)
1182        f.close()
1183        files.append(".gitattributes")
1184    run_command(GITS, ["add", "--"] + files)
1185
1186
1187def versions_from_parentdir(parentdir_prefix, root, verbose):
1188    """Try to determine the version from the parent directory name.
1189
1190    Source tarballs conventionally unpack into a directory that includes both
1191    the project name and a version string. We will also support searching up
1192    two directory levels for an appropriately named parent directory
1193    """
1194    rootdirs = []
1195
1196    for i in range(3):
1197        dirname = os.path.basename(root)
1198        if dirname.startswith(parentdir_prefix):
1199            return {
1200                "version": dirname[len(parentdir_prefix) :],
1201                "full-revisionid": None,
1202                "dirty": False,
1203                "error": None,
1204                "date": None,
1205            }
1206        else:
1207            rootdirs.append(root)
1208            root = os.path.dirname(root)  # up a level
1209
1210    if verbose:
1211        print(
1212            "Tried directories %s but none started with prefix %s"
1213            % (str(rootdirs), parentdir_prefix)
1214        )
1215    raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
1216
1217
1218SHORT_VERSION_PY = """
1219# This file was generated by 'versioneer.py' (0.19) from
1220# revision-control system data, or from the parent directory name of an
1221# unpacked source archive. Distribution tarballs contain a pre-generated copy
1222# of this file.
1223
1224import json
1225
1226version_json = '''
1227%s
1228'''  # END VERSION_JSON
1229
1230
1231def get_versions():
1232    return json.loads(version_json)
1233"""
1234
1235
1236def versions_from_file(filename):
1237    """Try to determine the version from _version.py if present."""
1238    try:
1239        with open(filename) as f:
1240            contents = f.read()
1241    except EnvironmentError:
1242        raise NotThisMethod("unable to read _version.py")
1243    mo = re.search(
1244        r"version_json = '''\n(.*)'''  # END VERSION_JSON", contents, re.M | re.S
1245    )
1246    if not mo:
1247        mo = re.search(
1248            r"version_json = '''\r\n(.*)'''  # END VERSION_JSON", contents, re.M | re.S
1249        )
1250    if not mo:
1251        raise NotThisMethod("no version_json in _version.py")
1252    return json.loads(mo.group(1))
1253
1254
1255def write_to_version_file(filename, versions):
1256    """Write the given version number to the given _version.py file."""
1257    os.unlink(filename)
1258    contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": "))
1259    with open(filename, "w") as f:
1260        f.write(SHORT_VERSION_PY % contents)
1261
1262    print("set %s to '%s'" % (filename, versions["version"]))
1263
1264
1265def plus_or_dot(pieces):
1266    """Return a + if we don't already have one, else return a ."""
1267    if "+" in pieces.get("closest-tag", ""):
1268        return "."
1269    return "+"
1270
1271
1272def render_pep440(pieces):
1273    """Build up version string, with post-release "local version identifier".
1274
1275    Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
1276    get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
1277
1278    Exceptions:
1279    1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
1280    """
1281    if pieces["closest-tag"]:
1282        rendered = pieces["closest-tag"]
1283        if pieces["distance"] or pieces["dirty"]:
1284            rendered += plus_or_dot(pieces)
1285            rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
1286            if pieces["dirty"]:
1287                rendered += ".dirty"
1288    else:
1289        # exception #1
1290        rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
1291        if pieces["dirty"]:
1292            rendered += ".dirty"
1293    return rendered
1294
1295
1296def render_pep440_pre(pieces):
1297    """TAG[.post0.devDISTANCE] -- No -dirty.
1298
1299    Exceptions:
1300    1: no tags. 0.post0.devDISTANCE
1301    """
1302    if pieces["closest-tag"]:
1303        rendered = pieces["closest-tag"]
1304        if pieces["distance"]:
1305            rendered += ".post0.dev%d" % pieces["distance"]
1306    else:
1307        # exception #1
1308        rendered = "0.post0.dev%d" % pieces["distance"]
1309    return rendered
1310
1311
1312def render_pep440_post(pieces):
1313    """TAG[.postDISTANCE[.dev0]+gHEX] .
1314
1315    The ".dev0" means dirty. Note that .dev0 sorts backwards
1316    (a dirty tree will appear "older" than the corresponding clean one),
1317    but you shouldn't be releasing software with -dirty anyways.
1318
1319    Exceptions:
1320    1: no tags. 0.postDISTANCE[.dev0]
1321    """
1322    if pieces["closest-tag"]:
1323        rendered = pieces["closest-tag"]
1324        if pieces["distance"] or pieces["dirty"]:
1325            rendered += ".post%d" % pieces["distance"]
1326            if pieces["dirty"]:
1327                rendered += ".dev0"
1328            rendered += plus_or_dot(pieces)
1329            rendered += "g%s" % pieces["short"]
1330    else:
1331        # exception #1
1332        rendered = "0.post%d" % pieces["distance"]
1333        if pieces["dirty"]:
1334            rendered += ".dev0"
1335        rendered += "+g%s" % pieces["short"]
1336    return rendered
1337
1338
1339def render_pep440_old(pieces):
1340    """TAG[.postDISTANCE[.dev0]] .
1341
1342    The ".dev0" means dirty.
1343
1344    Exceptions:
1345    1: no tags. 0.postDISTANCE[.dev0]
1346    """
1347    if pieces["closest-tag"]:
1348        rendered = pieces["closest-tag"]
1349        if pieces["distance"] or pieces["dirty"]:
1350            rendered += ".post%d" % pieces["distance"]
1351            if pieces["dirty"]:
1352                rendered += ".dev0"
1353    else:
1354        # exception #1
1355        rendered = "0.post%d" % pieces["distance"]
1356        if pieces["dirty"]:
1357            rendered += ".dev0"
1358    return rendered
1359
1360
1361def render_git_describe(pieces):
1362    """TAG[-DISTANCE-gHEX][-dirty].
1363
1364    Like 'git describe --tags --dirty --always'.
1365
1366    Exceptions:
1367    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
1368    """
1369    if pieces["closest-tag"]:
1370        rendered = pieces["closest-tag"]
1371        if pieces["distance"]:
1372            rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
1373    else:
1374        # exception #1
1375        rendered = pieces["short"]
1376    if pieces["dirty"]:
1377        rendered += "-dirty"
1378    return rendered
1379
1380
1381def render_git_describe_long(pieces):
1382    """TAG-DISTANCE-gHEX[-dirty].
1383
1384    Like 'git describe --tags --dirty --always -long'.
1385    The distance/hash is unconditional.
1386
1387    Exceptions:
1388    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
1389    """
1390    if pieces["closest-tag"]:
1391        rendered = pieces["closest-tag"]
1392        rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
1393    else:
1394        # exception #1
1395        rendered = pieces["short"]
1396    if pieces["dirty"]:
1397        rendered += "-dirty"
1398    return rendered
1399
1400
1401def render(pieces, style):
1402    """Render the given version pieces into the requested style."""
1403    if pieces["error"]:
1404        return {
1405            "version": "unknown",
1406            "full-revisionid": pieces.get("long"),
1407            "dirty": None,
1408            "error": pieces["error"],
1409            "date": None,
1410        }
1411
1412    if not style or style == "default":
1413        style = "pep440"  # the default
1414
1415    if style == "pep440":
1416        rendered = render_pep440(pieces)
1417    elif style == "pep440-pre":
1418        rendered = render_pep440_pre(pieces)
1419    elif style == "pep440-post":
1420        rendered = render_pep440_post(pieces)
1421    elif style == "pep440-old":
1422        rendered = render_pep440_old(pieces)
1423    elif style == "git-describe":
1424        rendered = render_git_describe(pieces)
1425    elif style == "git-describe-long":
1426        rendered = render_git_describe_long(pieces)
1427    else:
1428        raise ValueError("unknown style '%s'" % style)
1429
1430    return {
1431        "version": rendered,
1432        "full-revisionid": pieces["long"],
1433        "dirty": pieces["dirty"],
1434        "error": None,
1435        "date": pieces.get("date"),
1436    }
1437
1438
1439class VersioneerBadRootError(Exception):
1440    """The project root directory is unknown or missing key files."""
1441
1442
1443def get_versions(verbose=False):
1444    """Get the project version from whatever source is available.
1445
1446    Returns dict with two keys: 'version' and 'full'.
1447    """
1448    if "versioneer" in sys.modules:
1449        # see the discussion in cmdclass.py:get_cmdclass()
1450        del sys.modules["versioneer"]
1451
1452    root = get_root()
1453    cfg = get_config_from_root(root)
1454
1455    assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg"
1456    handlers = HANDLERS.get(cfg.VCS)
1457    assert handlers, "unrecognized VCS '%s'" % cfg.VCS
1458    verbose = verbose or cfg.verbose
1459    assert (
1460        cfg.versionfile_source is not None
1461    ), "please set versioneer.versionfile_source"
1462    assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix"
1463
1464    versionfile_abs = os.path.join(root, cfg.versionfile_source)
1465
1466    # extract version from first of: _version.py, VCS command (e.g. 'git
1467    # describe'), parentdir. This is meant to work for developers using a
1468    # source checkout, for users of a tarball created by 'setup.py sdist',
1469    # and for users of a tarball/zipball created by 'git archive' or github's
1470    # download-from-tag feature or the equivalent in other VCSes.
1471
1472    get_keywords_f = handlers.get("get_keywords")
1473    from_keywords_f = handlers.get("keywords")
1474    if get_keywords_f and from_keywords_f:
1475        try:
1476            keywords = get_keywords_f(versionfile_abs)
1477            ver = from_keywords_f(keywords, cfg.tag_prefix, verbose)
1478            if verbose:
1479                print("got version from expanded keyword %s" % ver)
1480            return ver
1481        except NotThisMethod:
1482            pass
1483
1484    try:
1485        ver = versions_from_file(versionfile_abs)
1486        if verbose:
1487            print("got version from file %s %s" % (versionfile_abs, ver))
1488        return ver
1489    except NotThisMethod:
1490        pass
1491
1492    from_vcs_f = handlers.get("pieces_from_vcs")
1493    if from_vcs_f:
1494        try:
1495            pieces = from_vcs_f(cfg.tag_prefix, root, verbose)
1496            ver = render(pieces, cfg.style)
1497            if verbose:
1498                print("got version from VCS %s" % ver)
1499            return ver
1500        except NotThisMethod:
1501            pass
1502
1503    try:
1504        if cfg.parentdir_prefix:
1505            ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
1506            if verbose:
1507                print("got version from parentdir %s" % ver)
1508            return ver
1509    except NotThisMethod:
1510        pass
1511
1512    if verbose:
1513        print("unable to compute version")
1514
1515    return {
1516        "version": "0+unknown",
1517        "full-revisionid": None,
1518        "dirty": None,
1519        "error": "unable to compute version",
1520        "date": None,
1521    }
1522
1523
1524def get_version():
1525    """Get the short version string for this project."""
1526    return get_versions()["version"]
1527
1528
1529def get_cmdclass(cmdclass=None):
1530    """Get the custom setuptools/distutils subclasses used by Versioneer.
1531
1532    If the package uses a different cmdclass (e.g. one from numpy), it
1533    should be provide as an argument.
1534    """
1535    if "versioneer" in sys.modules:
1536        del sys.modules["versioneer"]
1537        # this fixes the "python setup.py develop" case (also 'install' and
1538        # 'easy_install .'), in which subdependencies of the main project are
1539        # built (using setup.py bdist_egg) in the same python process. Assume
1540        # a main project A and a dependency B, which use different versions
1541        # of Versioneer. A's setup.py imports A's Versioneer, leaving it in
1542        # sys.modules by the time B's setup.py is executed, causing B to run
1543        # with the wrong versioneer. Setuptools wraps the sub-dep builds in a
1544        # sandbox that restores sys.modules to it's pre-build state, so the
1545        # parent is protected against the child's "import versioneer". By
1546        # removing ourselves from sys.modules here, before the child build
1547        # happens, we protect the child from the parent's versioneer too.
1548        # Also see https://github.com/python-versioneer/python-versioneer/issues/52
1549
1550    cmds = {} if cmdclass is None else cmdclass.copy()
1551
1552    # we add "version" to both distutils and setuptools
1553    from distutils.core import Command
1554
1555    class cmd_version(Command):
1556        description = "report generated version string"
1557        user_options = []
1558        boolean_options = []
1559
1560        def initialize_options(self):
1561            pass
1562
1563        def finalize_options(self):
1564            pass
1565
1566        def run(self):
1567            vers = get_versions(verbose=True)
1568            print("Version: %s" % vers["version"])
1569            print(" full-revisionid: %s" % vers.get("full-revisionid"))
1570            print(" dirty: %s" % vers.get("dirty"))
1571            print(" date: %s" % vers.get("date"))
1572            if vers["error"]:
1573                print(" error: %s" % vers["error"])
1574
1575    cmds["version"] = cmd_version
1576
1577    # we override "build_py" in both distutils and setuptools
1578    #
1579    # most invocation pathways end up running build_py:
1580    #  distutils/build -> build_py
1581    #  distutils/install -> distutils/build ->..
1582    #  setuptools/bdist_wheel -> distutils/install ->..
1583    #  setuptools/bdist_egg -> distutils/install_lib -> build_py
1584    #  setuptools/install -> bdist_egg ->..
1585    #  setuptools/develop -> ?
1586    #  pip install:
1587    #   copies source tree to a tempdir before running egg_info/etc
1588    #   if .git isn't copied too, 'git describe' will fail
1589    #   then does setup.py bdist_wheel, or sometimes setup.py install
1590    #  setup.py egg_info -> ?
1591
1592    # we override different "build_py" commands for both environments
1593    if "build_py" in cmds:
1594        _build_py = cmds["build_py"]
1595    elif "setuptools" in sys.modules:
1596        from setuptools.command.build_py import build_py as _build_py
1597    else:
1598        from distutils.command.build_py import build_py as _build_py
1599
1600    class cmd_build_py(_build_py):
1601        def run(self):
1602            root = get_root()
1603            cfg = get_config_from_root(root)
1604            versions = get_versions()
1605            _build_py.run(self)
1606            # now locate _version.py in the new build/ directory and replace
1607            # it with an updated value
1608            if cfg.versionfile_build:
1609                target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build)
1610                print("UPDATING %s" % target_versionfile)
1611                write_to_version_file(target_versionfile, versions)
1612
1613    cmds["build_py"] = cmd_build_py
1614
1615    if "setuptools" in sys.modules:
1616        from setuptools.command.build_ext import build_ext as _build_ext
1617    else:
1618        from distutils.command.build_ext import build_ext as _build_ext
1619
1620    class cmd_build_ext(_build_ext):
1621        def run(self):
1622            root = get_root()
1623            cfg = get_config_from_root(root)
1624            versions = get_versions()
1625            _build_ext.run(self)
1626            if self.inplace:
1627                # build_ext --inplace will only build extensions in
1628                # build/lib<..> dir with no _version.py to write to.
1629                # As in place builds will already have a _version.py
1630                # in the module dir, we do not need to write one.
1631                return
1632            # now locate _version.py in the new build/ directory and replace
1633            # it with an updated value
1634            target_versionfile = os.path.join(self.build_lib, cfg.versionfile_source)
1635            print("UPDATING %s" % target_versionfile)
1636            write_to_version_file(target_versionfile, versions)
1637
1638    cmds["build_ext"] = cmd_build_ext
1639
1640    if "cx_Freeze" in sys.modules:  # cx_freeze enabled?
1641        from cx_Freeze.dist import build_exe as _build_exe
1642
1643        # nczeczulin reports that py2exe won't like the pep440-style string
1644        # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g.
1645        # setup(console=[{
1646        #   "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION
1647        #   "product_version": versioneer.get_version(),
1648        #   ...
1649
1650        class cmd_build_exe(_build_exe):
1651            def run(self):
1652                root = get_root()
1653                cfg = get_config_from_root(root)
1654                versions = get_versions()
1655                target_versionfile = cfg.versionfile_source
1656                print("UPDATING %s" % target_versionfile)
1657                write_to_version_file(target_versionfile, versions)
1658
1659                _build_exe.run(self)
1660                os.unlink(target_versionfile)
1661                with open(cfg.versionfile_source, "w") as f:
1662                    LONG = LONG_VERSION_PY[cfg.VCS]
1663                    f.write(
1664                        LONG
1665                        % {
1666                            "DOLLAR": "$",
1667                            "STYLE": cfg.style,
1668                            "TAG_PREFIX": cfg.tag_prefix,
1669                            "PARENTDIR_PREFIX": cfg.parentdir_prefix,
1670                            "VERSIONFILE_SOURCE": cfg.versionfile_source,
1671                        }
1672                    )
1673
1674        cmds["build_exe"] = cmd_build_exe
1675        del cmds["build_py"]
1676
1677    if "py2exe" in sys.modules:  # py2exe enabled?
1678        from py2exe.distutils_buildexe import py2exe as _py2exe
1679
1680        class cmd_py2exe(_py2exe):
1681            def run(self):
1682                root = get_root()
1683                cfg = get_config_from_root(root)
1684                versions = get_versions()
1685                target_versionfile = cfg.versionfile_source
1686                print("UPDATING %s" % target_versionfile)
1687                write_to_version_file(target_versionfile, versions)
1688
1689                _py2exe.run(self)
1690                os.unlink(target_versionfile)
1691                with open(cfg.versionfile_source, "w") as f:
1692                    LONG = LONG_VERSION_PY[cfg.VCS]
1693                    f.write(
1694                        LONG
1695                        % {
1696                            "DOLLAR": "$",
1697                            "STYLE": cfg.style,
1698                            "TAG_PREFIX": cfg.tag_prefix,
1699                            "PARENTDIR_PREFIX": cfg.parentdir_prefix,
1700                            "VERSIONFILE_SOURCE": cfg.versionfile_source,
1701                        }
1702                    )
1703
1704        cmds["py2exe"] = cmd_py2exe
1705
1706    # we override different "sdist" commands for both environments
1707    if "sdist" in cmds:
1708        _sdist = cmds["sdist"]
1709    elif "setuptools" in sys.modules:
1710        from setuptools.command.sdist import sdist as _sdist
1711    else:
1712        from distutils.command.sdist import sdist as _sdist
1713
1714    class cmd_sdist(_sdist):
1715        def run(self):
1716            versions = get_versions()
1717            self._versioneer_generated_versions = versions
1718            # unless we update this, the command will keep using the old
1719            # version
1720            self.distribution.metadata.version = versions["version"]
1721            return _sdist.run(self)
1722
1723        def make_release_tree(self, base_dir, files):
1724            root = get_root()
1725            cfg = get_config_from_root(root)
1726            _sdist.make_release_tree(self, base_dir, files)
1727            # now locate _version.py in the new base_dir directory
1728            # (remembering that it may be a hardlink) and replace it with an
1729            # updated value
1730            target_versionfile = os.path.join(base_dir, cfg.versionfile_source)
1731            print("UPDATING %s" % target_versionfile)
1732            write_to_version_file(
1733                target_versionfile, self._versioneer_generated_versions
1734            )
1735
1736    cmds["sdist"] = cmd_sdist
1737
1738    return cmds
1739
1740
1741CONFIG_ERROR = """
1742setup.cfg is missing the necessary Versioneer configuration. You need
1743a section like:
1744
1745 [versioneer]
1746 VCS = git
1747 style = pep440
1748 versionfile_source = src/myproject/_version.py
1749 versionfile_build = myproject/_version.py
1750 tag_prefix =
1751 parentdir_prefix = myproject-
1752
1753You will also need to edit your setup.py to use the results:
1754
1755 import versioneer
1756 setup(version=versioneer.get_version(),
1757       cmdclass=versioneer.get_cmdclass(), ...)
1758
1759Please read the docstring in ./versioneer.py for configuration instructions,
1760edit setup.cfg, and re-run the installer or 'python versioneer.py setup'.
1761"""
1762
1763SAMPLE_CONFIG = """
1764# See the docstring in versioneer.py for instructions. Note that you must
1765# re-run 'versioneer.py setup' after changing this section, and commit the
1766# resulting files.
1767
1768[versioneer]
1769#VCS = git
1770#style = pep440
1771#versionfile_source =
1772#versionfile_build =
1773#tag_prefix =
1774#parentdir_prefix =
1775
1776"""
1777
1778INIT_PY_SNIPPET = """
1779from ._version import get_versions
1780__version__ = get_versions()['version']
1781del get_versions
1782"""
1783
1784
1785def do_setup():
1786    """Do main VCS-independent setup function for installing Versioneer."""
1787    root = get_root()
1788    try:
1789        cfg = get_config_from_root(root)
1790    except (
1791        EnvironmentError,
1792        configparser.NoSectionError,
1793        configparser.NoOptionError,
1794    ) as e:
1795        if isinstance(e, (EnvironmentError, configparser.NoSectionError)):
1796            print("Adding sample versioneer config to setup.cfg", file=sys.stderr)
1797            with open(os.path.join(root, "setup.cfg"), "a") as f:
1798                f.write(SAMPLE_CONFIG)
1799        print(CONFIG_ERROR, file=sys.stderr)
1800        return 1
1801
1802    print(" creating %s" % cfg.versionfile_source)
1803    with open(cfg.versionfile_source, "w") as f:
1804        LONG = LONG_VERSION_PY[cfg.VCS]
1805        f.write(
1806            LONG
1807            % {
1808                "DOLLAR": "$",
1809                "STYLE": cfg.style,
1810                "TAG_PREFIX": cfg.tag_prefix,
1811                "PARENTDIR_PREFIX": cfg.parentdir_prefix,
1812                "VERSIONFILE_SOURCE": cfg.versionfile_source,
1813            }
1814        )
1815
1816    ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py")
1817    if os.path.exists(ipy):
1818        try:
1819            with open(ipy, "r") as f:
1820                old = f.read()
1821        except EnvironmentError:
1822            old = ""
1823        if INIT_PY_SNIPPET not in old:
1824            print(" appending to %s" % ipy)
1825            with open(ipy, "a") as f:
1826                f.write(INIT_PY_SNIPPET)
1827        else:
1828            print(" %s unmodified" % ipy)
1829    else:
1830        print(" %s doesn't exist, ok" % ipy)
1831        ipy = None
1832
1833    # Make sure both the top-level "versioneer.py" and versionfile_source
1834    # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so
1835    # they'll be copied into source distributions. Pip won't be able to
1836    # install the package without this.
1837    manifest_in = os.path.join(root, "MANIFEST.in")
1838    simple_includes = set()
1839    try:
1840        with open(manifest_in, "r") as f:
1841            for line in f:
1842                if line.startswith("include "):
1843                    for include in line.split()[1:]:
1844                        simple_includes.add(include)
1845    except EnvironmentError:
1846        pass
1847    # That doesn't cover everything MANIFEST.in can do
1848    # (http://docs.python.org/2/distutils/sourcedist.html#commands), so
1849    # it might give some false negatives. Appending redundant 'include'
1850    # lines is safe, though.
1851    if "versioneer.py" not in simple_includes:
1852        print(" appending 'versioneer.py' to MANIFEST.in")
1853        with open(manifest_in, "a") as f:
1854            f.write("include versioneer.py\n")
1855    else:
1856        print(" 'versioneer.py' already in MANIFEST.in")
1857    if cfg.versionfile_source not in simple_includes:
1858        print(
1859            " appending versionfile_source ('%s') to MANIFEST.in"
1860            % cfg.versionfile_source
1861        )
1862        with open(manifest_in, "a") as f:
1863            f.write("include %s\n" % cfg.versionfile_source)
1864    else:
1865        print(" versionfile_source already in MANIFEST.in")
1866
1867    # Make VCS-specific changes. For git, this means creating/changing
1868    # .gitattributes to mark _version.py for export-subst keyword
1869    # substitution.
1870    do_vcs_install(manifest_in, cfg.versionfile_source, ipy)
1871    return 0
1872
1873
1874def scan_setup_py():
1875    """Validate the contents of setup.py against Versioneer's expectations."""
1876    found = set()
1877    setters = False
1878    errors = 0
1879    with open("setup.py", "r") as f:
1880        for line in f.readlines():
1881            if "import versioneer" in line:
1882                found.add("import")
1883            if "versioneer.get_cmdclass()" in line:
1884                found.add("cmdclass")
1885            if "versioneer.get_version()" in line:
1886                found.add("get_version")
1887            if "versioneer.VCS" in line:
1888                setters = True
1889            if "versioneer.versionfile_source" in line:
1890                setters = True
1891    if len(found) != 3:
1892        print("")
1893        print("Your setup.py appears to be missing some important items")
1894        print("(but I might be wrong). Please make sure it has something")
1895        print("roughly like the following:")
1896        print("")
1897        print(" import versioneer")
1898        print(" setup( version=versioneer.get_version(),")
1899        print("        cmdclass=versioneer.get_cmdclass(),  ...)")
1900        print("")
1901        errors += 1
1902    if setters:
1903        print("You should remove lines like 'versioneer.VCS = ' and")
1904        print("'versioneer.versionfile_source = ' . This configuration")
1905        print("now lives in setup.cfg, and should be removed from setup.py")
1906        print("")
1907        errors += 1
1908    return errors
1909
1910
1911if __name__ == "__main__":
1912    cmd = sys.argv[1]
1913    if cmd == "setup":
1914        errors = do_setup()
1915        errors += scan_setup_py()
1916        if errors:
1917            sys.exit(1)
1918