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