1# Version: 0.20
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* [versioningit](https://github.com/jwodder/versioningit) - a PEP 518-based setuptools
259  plugin
260
261## License
262
263To make Versioneer easier to embed, all its code is dedicated to the public
264domain. The `_version.py` that it creates is also in the public domain.
265Specifically, both are released under the Creative Commons "Public Domain
266Dedication" license (CC0-1.0), as described in
267https://creativecommons.org/publicdomain/zero/1.0/ .
268
269[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg
270[pypi-url]: https://pypi.python.org/pypi/versioneer/
271[travis-image]:
272https://img.shields.io/travis/com/python-versioneer/python-versioneer.svg
273[travis-url]: https://travis-ci.com/github/python-versioneer/python-versioneer
274
275"""
276
277import configparser
278import errno
279import json
280import os
281import re
282import subprocess
283import sys
284
285
286class VersioneerConfig:  # pylint: disable=too-few-public-methods # noqa
287    """Container for Versioneer configuration parameters."""
288
289
290def get_root():
291    """Get the project root directory.
292
293    We require that all commands are run from the project root, i.e. the
294    directory that contains setup.py, setup.cfg, and versioneer.py .
295    """
296    root = os.path.realpath(os.path.abspath(os.getcwd()))
297    setup_py = os.path.join(root, "setup.py")
298    versioneer_py = os.path.join(root, "versioneer.py")
299    if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)):
300        # allow 'python path/to/setup.py COMMAND'
301        root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0])))
302        setup_py = os.path.join(root, "setup.py")
303        versioneer_py = os.path.join(root, "versioneer.py")
304    if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)):
305        err = (
306            "Versioneer was unable to run the project root directory. "
307            "Versioneer requires setup.py to be executed from "
308            "its immediate directory (like 'python setup.py COMMAND'), "
309            "or in a way that lets it use sys.argv[0] to find the root "
310            "(like 'python path/to/setup.py COMMAND')."
311        )
312        raise VersioneerBadRootError(err)
313    try:
314        # Certain runtime workflows (setup.py install/develop in a setuptools
315        # tree) execute all dependencies in a single python process, so
316        # "versioneer" may be imported multiple times, and python's shared
317        # module-import table will cache the first one. So we can't use
318        # os.path.dirname(__file__), as that will find whichever
319        # versioneer.py was first imported, even in later projects.
320        my_path = os.path.realpath(os.path.abspath(__file__))
321        me_dir = os.path.normcase(os.path.splitext(my_path)[0])
322        vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0])
323        if me_dir != vsr_dir:
324            print(
325                "Warning: build in %s is using versioneer.py from %s"
326                % (os.path.dirname(my_path), versioneer_py)
327            )
328    except NameError:
329        pass
330    return root
331
332
333def get_config_from_root(root):
334    """Read the project setup.cfg file to determine Versioneer config."""
335    # This might raise EnvironmentError (if setup.cfg is missing), or
336    # configparser.NoSectionError (if it lacks a [versioneer] section), or
337    # configparser.NoOptionError (if it lacks "VCS="). See the docstring at
338    # the top of versioneer.py for instructions on writing your setup.cfg .
339    setup_cfg = os.path.join(root, "setup.cfg")
340    parser = configparser.ConfigParser()
341    with open(setup_cfg, "r") as cfg_file:
342        parser.read_file(cfg_file)
343    VCS = parser.get("versioneer", "VCS")  # mandatory
344
345    # Dict-like interface for non-mandatory entries
346    section = parser["versioneer"]
347
348    # pylint:disable=attribute-defined-outside-init # noqa
349    cfg = VersioneerConfig()
350    cfg.VCS = VCS
351    cfg.style = section.get("style", "")
352    cfg.versionfile_source = section.get("versionfile_source")
353    cfg.versionfile_build = section.get("versionfile_build")
354    cfg.tag_prefix = section.get("tag_prefix")
355    if cfg.tag_prefix in ("''", '""'):
356        cfg.tag_prefix = ""
357    cfg.parentdir_prefix = section.get("parentdir_prefix")
358    cfg.verbose = section.get("verbose")
359    return cfg
360
361
362class NotThisMethod(Exception):
363    """Exception raised if a method is not valid for the current scenario."""
364
365
366# these dictionaries contain VCS-specific tools
367LONG_VERSION_PY = {}
368HANDLERS = {}
369
370
371def register_vcs_handler(vcs, method):  # decorator
372    """Create decorator to mark a method as the handler of a VCS."""
373
374    def decorate(f):
375        """Store f in HANDLERS[vcs][method]."""
376        HANDLERS.setdefault(vcs, {})[method] = f
377        return f
378
379    return decorate
380
381
382# pylint:disable=too-many-arguments,consider-using-with # noqa
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    process = None
387    for command in commands:
388        try:
389            dispcmd = str([command] + args)
390            # remember shell=False, so use git.cmd on windows, not just git
391            process = subprocess.Popen(
392                [command] + 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 = process.communicate()[0].strip().decode()
412    if process.returncode != 0:
413        if verbose:
414            print("unable to run %s (error)" % dispcmd)
415            print("stdout was %s" % stdout)
416        return None, process.returncode
417    return stdout, process.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.20 (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:  # pylint: disable=too-few-public-methods
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
491# pylint:disable=too-many-arguments,consider-using-with # noqa
492def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
493                env=None):
494    """Call the given command(s)."""
495    assert isinstance(commands, list)
496    process = None
497    for command in commands:
498        try:
499            dispcmd = str([command] + args)
500            # remember shell=False, so use git.cmd on windows, not just git
501            process = subprocess.Popen([command] + args, cwd=cwd, env=env,
502                                       stdout=subprocess.PIPE,
503                                       stderr=(subprocess.PIPE if hide_stderr
504                                               else None))
505            break
506        except EnvironmentError:
507            e = sys.exc_info()[1]
508            if e.errno == errno.ENOENT:
509                continue
510            if verbose:
511                print("unable to run %%s" %% dispcmd)
512                print(e)
513            return None, None
514    else:
515        if verbose:
516            print("unable to find command, tried %%s" %% (commands,))
517        return None, None
518    stdout = process.communicate()[0].strip().decode()
519    if process.returncode != 0:
520        if verbose:
521            print("unable to run %%s (error)" %% dispcmd)
522            print("stdout was %%s" %% stdout)
523        return None, process.returncode
524    return stdout, process.returncode
525
526
527def versions_from_parentdir(parentdir_prefix, root, verbose):
528    """Try to determine the version from the parent directory name.
529
530    Source tarballs conventionally unpack into a directory that includes both
531    the project name and a version string. We will also support searching up
532    two directory levels for an appropriately named parent directory
533    """
534    rootdirs = []
535
536    for _ in range(3):
537        dirname = os.path.basename(root)
538        if dirname.startswith(parentdir_prefix):
539            return {"version": dirname[len(parentdir_prefix):],
540                    "full-revisionid": None,
541                    "dirty": False, "error": None, "date": None}
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        with open(versionfile_abs, "r") as fobj:
561            for line in fobj:
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    except EnvironmentError:
575        pass
576    return keywords
577
578
579@register_vcs_handler("git", "keywords")
580def git_versions_from_keywords(keywords, tag_prefix, verbose):
581    """Get version information from git keywords."""
582    if "refnames" not in keywords:
583        raise NotThisMethod("Short version file found")
584    date = keywords.get("date")
585    if date is not None:
586        # Use only the last line.  Previous lines may contain GPG signature
587        # information.
588        date = date.splitlines()[-1]
589
590        # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant
591        # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601
592        # -like" string, which we must then edit to make compliant), because
593        # it's been around since git-1.5.3, and it's too difficult to
594        # discover which version we're using, or to work around using an
595        # older one.
596        date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
597    refnames = keywords["refnames"].strip()
598    if refnames.startswith("$Format"):
599        if verbose:
600            print("keywords are unexpanded, not using")
601        raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
602    refs = {r.strip() for r in refnames.strip("()").split(",")}
603    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
604    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
605    TAG = "tag: "
606    tags = {r[len(TAG):] for r in refs if r.startswith(TAG)}
607    if not tags:
608        # Either we're using git < 1.8.3, or there really are no tags. We use
609        # a heuristic: assume all version tags have a digit. The old git %%d
610        # expansion behaves like git log --decorate=short and strips out the
611        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
612        # between branches and tags. By ignoring refnames without digits, we
613        # filter out many common branch names like "release" and
614        # "stabilization", as well as "HEAD" and "master".
615        tags = {r for r in refs if re.search(r'\d', r)}
616        if verbose:
617            print("discarding '%%s', no digits" %% ",".join(refs - tags))
618    if verbose:
619        print("likely tags: %%s" %% ",".join(sorted(tags)))
620    for ref in sorted(tags):
621        # sorting will prefer e.g. "2.0" over "2.0rc1"
622        if ref.startswith(tag_prefix):
623            r = ref[len(tag_prefix):]
624            # Filter out refs that exactly match prefix or that don't start
625            # with a number once the prefix is stripped (mostly a concern
626            # when prefix is '')
627            if not re.match(r'\d', r):
628                continue
629            if verbose:
630                print("picking %%s" %% r)
631            return {"version": r,
632                    "full-revisionid": keywords["full"].strip(),
633                    "dirty": False, "error": None,
634                    "date": date}
635    # no suitable tags, so version is "0+unknown", but full hex is still there
636    if verbose:
637        print("no suitable tags, using unknown + full revision id")
638    return {"version": "0+unknown",
639            "full-revisionid": keywords["full"].strip(),
640            "dirty": False, "error": "no suitable tags", "date": None}
641
642
643@register_vcs_handler("git", "pieces_from_vcs")
644def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
645    """Get version from 'git describe' in the root of the source tree.
646
647    This only gets called if the git-archive 'subst' keywords were *not*
648    expanded, and _version.py hasn't already been rewritten with a short
649    version string, meaning we're inside a checked out source tree.
650    """
651    GITS = ["git"]
652    if sys.platform == "win32":
653        GITS = ["git.cmd", "git.exe"]
654
655    _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
656                   hide_stderr=True)
657    if rc != 0:
658        if verbose:
659            print("Directory %%s not under git control" %% root)
660        raise NotThisMethod("'git rev-parse --git-dir' returned error")
661
662    # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
663    # if there isn't one, this yields HEX[-dirty] (no NUM)
664    describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty",
665                                     "--always", "--long",
666                                     "--match", "%%s*" %% tag_prefix],
667                              cwd=root)
668    # --long was added in git-1.5.5
669    if describe_out is None:
670        raise NotThisMethod("'git describe' failed")
671    describe_out = describe_out.strip()
672    full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
673    if full_out is None:
674        raise NotThisMethod("'git rev-parse' failed")
675    full_out = full_out.strip()
676
677    pieces = {}
678    pieces["long"] = full_out
679    pieces["short"] = full_out[:7]  # maybe improved later
680    pieces["error"] = None
681
682    branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"],
683                             cwd=root)
684    # --abbrev-ref was added in git-1.6.3
685    if rc != 0 or branch_name is None:
686        raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
687    branch_name = branch_name.strip()
688
689    if branch_name == "HEAD":
690        # If we aren't exactly on a branch, pick a branch which represents
691        # the current commit. If all else fails, we are on a branchless
692        # commit.
693        branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
694        # --contains was added in git-1.5.4
695        if rc != 0 or branches is None:
696            raise NotThisMethod("'git branch --contains' returned error")
697        branches = branches.split("\n")
698
699        # Remove the first line if we're running detached
700        if "(" in branches[0]:
701            branches.pop(0)
702
703        # Strip off the leading "* " from the list of branches.
704        branches = [branch[2:] for branch in branches]
705        if "master" in branches:
706            branch_name = "master"
707        elif not branches:
708            branch_name = None
709        else:
710            # Pick the first branch that is returned. Good or bad.
711            branch_name = branches[0]
712
713    pieces["branch"] = branch_name
714
715    # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
716    # TAG might have hyphens.
717    git_describe = describe_out
718
719    # look for -dirty suffix
720    dirty = git_describe.endswith("-dirty")
721    pieces["dirty"] = dirty
722    if dirty:
723        git_describe = git_describe[:git_describe.rindex("-dirty")]
724
725    # now we have TAG-NUM-gHEX or HEX
726
727    if "-" in git_describe:
728        # TAG-NUM-gHEX
729        mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
730        if not mo:
731            # unparseable. Maybe git-describe is misbehaving?
732            pieces["error"] = ("unable to parse git-describe output: '%%s'"
733                               %% describe_out)
734            return pieces
735
736        # tag
737        full_tag = mo.group(1)
738        if not full_tag.startswith(tag_prefix):
739            if verbose:
740                fmt = "tag '%%s' doesn't start with prefix '%%s'"
741                print(fmt %% (full_tag, tag_prefix))
742            pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'"
743                               %% (full_tag, tag_prefix))
744            return pieces
745        pieces["closest-tag"] = full_tag[len(tag_prefix):]
746
747        # distance: number of commits since tag
748        pieces["distance"] = int(mo.group(2))
749
750        # commit: short hex revision ID
751        pieces["short"] = mo.group(3)
752
753    else:
754        # HEX: no tags
755        pieces["closest-tag"] = None
756        count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
757        pieces["distance"] = int(count_out)  # total number of commits
758
759    # commit date: see ISO-8601 comment in git_versions_from_keywords()
760    date = runner(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip()
761    # Use only the last line.  Previous lines may contain GPG signature
762    # information.
763    date = date.splitlines()[-1]
764    pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
765
766    return pieces
767
768
769def plus_or_dot(pieces):
770    """Return a + if we don't already have one, else return a ."""
771    if "+" in pieces.get("closest-tag", ""):
772        return "."
773    return "+"
774
775
776def render_pep440(pieces):
777    """Build up version string, with post-release "local version identifier".
778
779    Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
780    get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
781
782    Exceptions:
783    1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
784    """
785    if pieces["closest-tag"]:
786        rendered = pieces["closest-tag"]
787        if pieces["distance"] or pieces["dirty"]:
788            rendered += plus_or_dot(pieces)
789            rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"])
790            if pieces["dirty"]:
791                rendered += ".dirty"
792    else:
793        # exception #1
794        rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"],
795                                          pieces["short"])
796        if pieces["dirty"]:
797            rendered += ".dirty"
798    return rendered
799
800
801def render_pep440_branch(pieces):
802    """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
803
804    The ".dev0" means not master branch. Note that .dev0 sorts backwards
805    (a feature branch will appear "older" than the master branch).
806
807    Exceptions:
808    1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
809    """
810    if pieces["closest-tag"]:
811        rendered = pieces["closest-tag"]
812        if pieces["distance"] or pieces["dirty"]:
813            if pieces["branch"] != "master":
814                rendered += ".dev0"
815            rendered += plus_or_dot(pieces)
816            rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"])
817            if pieces["dirty"]:
818                rendered += ".dirty"
819    else:
820        # exception #1
821        rendered = "0"
822        if pieces["branch"] != "master":
823            rendered += ".dev0"
824        rendered += "+untagged.%%d.g%%s" %% (pieces["distance"],
825                                          pieces["short"])
826        if pieces["dirty"]:
827            rendered += ".dirty"
828    return rendered
829
830
831def render_pep440_pre(pieces):
832    """TAG[.post0.devDISTANCE] -- No -dirty.
833
834    Exceptions:
835    1: no tags. 0.post0.devDISTANCE
836    """
837    if pieces["closest-tag"]:
838        rendered = pieces["closest-tag"]
839        if pieces["distance"]:
840            rendered += ".post0.dev%%d" %% pieces["distance"]
841    else:
842        # exception #1
843        rendered = "0.post0.dev%%d" %% pieces["distance"]
844    return rendered
845
846
847def render_pep440_post(pieces):
848    """TAG[.postDISTANCE[.dev0]+gHEX] .
849
850    The ".dev0" means dirty. Note that .dev0 sorts backwards
851    (a dirty tree will appear "older" than the corresponding clean one),
852    but you shouldn't be releasing software with -dirty anyways.
853
854    Exceptions:
855    1: no tags. 0.postDISTANCE[.dev0]
856    """
857    if pieces["closest-tag"]:
858        rendered = pieces["closest-tag"]
859        if pieces["distance"] or pieces["dirty"]:
860            rendered += ".post%%d" %% pieces["distance"]
861            if pieces["dirty"]:
862                rendered += ".dev0"
863            rendered += plus_or_dot(pieces)
864            rendered += "g%%s" %% pieces["short"]
865    else:
866        # exception #1
867        rendered = "0.post%%d" %% pieces["distance"]
868        if pieces["dirty"]:
869            rendered += ".dev0"
870        rendered += "+g%%s" %% pieces["short"]
871    return rendered
872
873
874def render_pep440_post_branch(pieces):
875    """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
876
877    The ".dev0" means not master branch.
878
879    Exceptions:
880    1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
881    """
882    if pieces["closest-tag"]:
883        rendered = pieces["closest-tag"]
884        if pieces["distance"] or pieces["dirty"]:
885            rendered += ".post%%d" %% pieces["distance"]
886            if pieces["branch"] != "master":
887                rendered += ".dev0"
888            rendered += plus_or_dot(pieces)
889            rendered += "g%%s" %% pieces["short"]
890            if pieces["dirty"]:
891                rendered += ".dirty"
892    else:
893        # exception #1
894        rendered = "0.post%%d" %% pieces["distance"]
895        if pieces["branch"] != "master":
896            rendered += ".dev0"
897        rendered += "+g%%s" %% pieces["short"]
898        if pieces["dirty"]:
899            rendered += ".dirty"
900    return rendered
901
902
903def render_pep440_old(pieces):
904    """TAG[.postDISTANCE[.dev0]] .
905
906    The ".dev0" means dirty.
907
908    Exceptions:
909    1: no tags. 0.postDISTANCE[.dev0]
910    """
911    if pieces["closest-tag"]:
912        rendered = pieces["closest-tag"]
913        if pieces["distance"] or pieces["dirty"]:
914            rendered += ".post%%d" %% pieces["distance"]
915            if pieces["dirty"]:
916                rendered += ".dev0"
917    else:
918        # exception #1
919        rendered = "0.post%%d" %% pieces["distance"]
920        if pieces["dirty"]:
921            rendered += ".dev0"
922    return rendered
923
924
925def render_git_describe(pieces):
926    """TAG[-DISTANCE-gHEX][-dirty].
927
928    Like 'git describe --tags --dirty --always'.
929
930    Exceptions:
931    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
932    """
933    if pieces["closest-tag"]:
934        rendered = pieces["closest-tag"]
935        if pieces["distance"]:
936            rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
937    else:
938        # exception #1
939        rendered = pieces["short"]
940    if pieces["dirty"]:
941        rendered += "-dirty"
942    return rendered
943
944
945def render_git_describe_long(pieces):
946    """TAG-DISTANCE-gHEX[-dirty].
947
948    Like 'git describe --tags --dirty --always -long'.
949    The distance/hash is unconditional.
950
951    Exceptions:
952    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
953    """
954    if pieces["closest-tag"]:
955        rendered = pieces["closest-tag"]
956        rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
957    else:
958        # exception #1
959        rendered = pieces["short"]
960    if pieces["dirty"]:
961        rendered += "-dirty"
962    return rendered
963
964
965def render(pieces, style):
966    """Render the given version pieces into the requested style."""
967    if pieces["error"]:
968        return {"version": "unknown",
969                "full-revisionid": pieces.get("long"),
970                "dirty": None,
971                "error": pieces["error"],
972                "date": None}
973
974    if not style or style == "default":
975        style = "pep440"  # the default
976
977    if style == "pep440":
978        rendered = render_pep440(pieces)
979    elif style == "pep440-branch":
980        rendered = render_pep440_branch(pieces)
981    elif style == "pep440-pre":
982        rendered = render_pep440_pre(pieces)
983    elif style == "pep440-post":
984        rendered = render_pep440_post(pieces)
985    elif style == "pep440-post-branch":
986        rendered = render_pep440_post_branch(pieces)
987    elif style == "pep440-old":
988        rendered = render_pep440_old(pieces)
989    elif style == "git-describe":
990        rendered = render_git_describe(pieces)
991    elif style == "git-describe-long":
992        rendered = render_git_describe_long(pieces)
993    else:
994        raise ValueError("unknown style '%%s'" %% style)
995
996    return {"version": rendered, "full-revisionid": pieces["long"],
997            "dirty": pieces["dirty"], "error": None,
998            "date": pieces.get("date")}
999
1000
1001def get_versions():
1002    """Get version information or return default if unable to do so."""
1003    # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
1004    # __file__, we can work backwards from there to the root. Some
1005    # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
1006    # case we can only use expanded keywords.
1007
1008    cfg = get_config()
1009    verbose = cfg.verbose
1010
1011    try:
1012        return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
1013                                          verbose)
1014    except NotThisMethod:
1015        pass
1016
1017    try:
1018        root = os.path.realpath(__file__)
1019        # versionfile_source is the relative path from the top of the source
1020        # tree (where the .git directory might live) to this file. Invert
1021        # this to find the root from __file__.
1022        for _ in cfg.versionfile_source.split('/'):
1023            root = os.path.dirname(root)
1024    except NameError:
1025        return {"version": "0+unknown", "full-revisionid": None,
1026                "dirty": None,
1027                "error": "unable to find root of source tree",
1028                "date": None}
1029
1030    try:
1031        pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
1032        return render(pieces, cfg.style)
1033    except NotThisMethod:
1034        pass
1035
1036    try:
1037        if cfg.parentdir_prefix:
1038            return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
1039    except NotThisMethod:
1040        pass
1041
1042    return {"version": "0+unknown", "full-revisionid": None,
1043            "dirty": None,
1044            "error": "unable to compute version", "date": None}
1045'''
1046
1047
1048@register_vcs_handler("git", "get_keywords")
1049def git_get_keywords(versionfile_abs):
1050    """Extract version information from the given file."""
1051    # the code embedded in _version.py can just fetch the value of these
1052    # keywords. When used from setup.py, we don't want to import _version.py,
1053    # so we do it with a regexp instead. This function is not used from
1054    # _version.py.
1055    keywords = {}
1056    try:
1057        with open(versionfile_abs, "r") as fobj:
1058            for line in fobj:
1059                if line.strip().startswith("git_refnames ="):
1060                    mo = re.search(r'=\s*"(.*)"', line)
1061                    if mo:
1062                        keywords["refnames"] = mo.group(1)
1063                if line.strip().startswith("git_full ="):
1064                    mo = re.search(r'=\s*"(.*)"', line)
1065                    if mo:
1066                        keywords["full"] = mo.group(1)
1067                if line.strip().startswith("git_date ="):
1068                    mo = re.search(r'=\s*"(.*)"', line)
1069                    if mo:
1070                        keywords["date"] = mo.group(1)
1071    except EnvironmentError:
1072        pass
1073    return keywords
1074
1075
1076@register_vcs_handler("git", "keywords")
1077def git_versions_from_keywords(keywords, tag_prefix, verbose):
1078    """Get version information from git keywords."""
1079    if "refnames" not in keywords:
1080        raise NotThisMethod("Short version file found")
1081    date = keywords.get("date")
1082    if date is not None:
1083        # Use only the last line.  Previous lines may contain GPG signature
1084        # information.
1085        date = date.splitlines()[-1]
1086
1087        # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
1088        # datestamp. However we prefer "%ci" (which expands to an "ISO-8601
1089        # -like" string, which we must then edit to make compliant), because
1090        # it's been around since git-1.5.3, and it's too difficult to
1091        # discover which version we're using, or to work around using an
1092        # older one.
1093        date = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
1094    refnames = keywords["refnames"].strip()
1095    if refnames.startswith("$Format"):
1096        if verbose:
1097            print("keywords are unexpanded, not using")
1098        raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
1099    refs = {r.strip() for r in refnames.strip("()").split(",")}
1100    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
1101    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
1102    TAG = "tag: "
1103    tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)}
1104    if not tags:
1105        # Either we're using git < 1.8.3, or there really are no tags. We use
1106        # a heuristic: assume all version tags have a digit. The old git %d
1107        # expansion behaves like git log --decorate=short and strips out the
1108        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
1109        # between branches and tags. By ignoring refnames without digits, we
1110        # filter out many common branch names like "release" and
1111        # "stabilization", as well as "HEAD" and "master".
1112        tags = {r for r in refs if re.search(r"\d", r)}
1113        if verbose:
1114            print("discarding '%s', no digits" % ",".join(refs - tags))
1115    if verbose:
1116        print("likely tags: %s" % ",".join(sorted(tags)))
1117    for ref in sorted(tags):
1118        # sorting will prefer e.g. "2.0" over "2.0rc1"
1119        if ref.startswith(tag_prefix):
1120            r = ref[len(tag_prefix) :]
1121            # Filter out refs that exactly match prefix or that don't start
1122            # with a number once the prefix is stripped (mostly a concern
1123            # when prefix is '')
1124            if not re.match(r"\d", r):
1125                continue
1126            if verbose:
1127                print("picking %s" % r)
1128            return {
1129                "version": r,
1130                "full-revisionid": keywords["full"].strip(),
1131                "dirty": False,
1132                "error": None,
1133                "date": date,
1134            }
1135    # no suitable tags, so version is "0+unknown", but full hex is still there
1136    if verbose:
1137        print("no suitable tags, using unknown + full revision id")
1138    return {
1139        "version": "0+unknown",
1140        "full-revisionid": keywords["full"].strip(),
1141        "dirty": False,
1142        "error": "no suitable tags",
1143        "date": None,
1144    }
1145
1146
1147@register_vcs_handler("git", "pieces_from_vcs")
1148def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
1149    """Get version from 'git describe' in the root of the source tree.
1150
1151    This only gets called if the git-archive 'subst' keywords were *not*
1152    expanded, and _version.py hasn't already been rewritten with a short
1153    version string, meaning we're inside a checked out source tree.
1154    """
1155    GITS = ["git"]
1156    if sys.platform == "win32":
1157        GITS = ["git.cmd", "git.exe"]
1158
1159    _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True)
1160    if rc != 0:
1161        if verbose:
1162            print("Directory %s not under git control" % root)
1163        raise NotThisMethod("'git rev-parse --git-dir' returned error")
1164
1165    # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
1166    # if there isn't one, this yields HEX[-dirty] (no NUM)
1167    describe_out, rc = runner(
1168        GITS,
1169        [
1170            "describe",
1171            "--tags",
1172            "--dirty",
1173            "--always",
1174            "--long",
1175            "--match",
1176            "%s*" % tag_prefix,
1177        ],
1178        cwd=root,
1179    )
1180    # --long was added in git-1.5.5
1181    if describe_out is None:
1182        raise NotThisMethod("'git describe' failed")
1183    describe_out = describe_out.strip()
1184    full_out, rc = runner(GITS, ["rev-parse", "HEAD"], cwd=root)
1185    if full_out is None:
1186        raise NotThisMethod("'git rev-parse' failed")
1187    full_out = full_out.strip()
1188
1189    pieces = {}
1190    pieces["long"] = full_out
1191    pieces["short"] = full_out[:7]  # maybe improved later
1192    pieces["error"] = None
1193
1194    branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root)
1195    # --abbrev-ref was added in git-1.6.3
1196    if rc != 0 or branch_name is None:
1197        raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
1198    branch_name = branch_name.strip()
1199
1200    if branch_name == "HEAD":
1201        # If we aren't exactly on a branch, pick a branch which represents
1202        # the current commit. If all else fails, we are on a branchless
1203        # commit.
1204        branches, rc = runner(GITS, ["branch", "--contains"], cwd=root)
1205        # --contains was added in git-1.5.4
1206        if rc != 0 or branches is None:
1207            raise NotThisMethod("'git branch --contains' returned error")
1208        branches = branches.split("\n")
1209
1210        # Remove the first line if we're running detached
1211        if "(" in branches[0]:
1212            branches.pop(0)
1213
1214        # Strip off the leading "* " from the list of branches.
1215        branches = [branch[2:] for branch in branches]
1216        if "master" in branches:
1217            branch_name = "master"
1218        elif not branches:
1219            branch_name = None
1220        else:
1221            # Pick the first branch that is returned. Good or bad.
1222            branch_name = branches[0]
1223
1224    pieces["branch"] = branch_name
1225
1226    # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
1227    # TAG might have hyphens.
1228    git_describe = describe_out
1229
1230    # look for -dirty suffix
1231    dirty = git_describe.endswith("-dirty")
1232    pieces["dirty"] = dirty
1233    if dirty:
1234        git_describe = git_describe[: git_describe.rindex("-dirty")]
1235
1236    # now we have TAG-NUM-gHEX or HEX
1237
1238    if "-" in git_describe:
1239        # TAG-NUM-gHEX
1240        mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe)
1241        if not mo:
1242            # unparseable. Maybe git-describe is misbehaving?
1243            pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out
1244            return pieces
1245
1246        # tag
1247        full_tag = mo.group(1)
1248        if not full_tag.startswith(tag_prefix):
1249            if verbose:
1250                fmt = "tag '%s' doesn't start with prefix '%s'"
1251                print(fmt % (full_tag, tag_prefix))
1252            pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (
1253                full_tag,
1254                tag_prefix,
1255            )
1256            return pieces
1257        pieces["closest-tag"] = full_tag[len(tag_prefix) :]
1258
1259        # distance: number of commits since tag
1260        pieces["distance"] = int(mo.group(2))
1261
1262        # commit: short hex revision ID
1263        pieces["short"] = mo.group(3)
1264
1265    else:
1266        # HEX: no tags
1267        pieces["closest-tag"] = None
1268        count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
1269        pieces["distance"] = int(count_out)  # total number of commits
1270
1271    # commit date: see ISO-8601 comment in git_versions_from_keywords()
1272    date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip()
1273    # Use only the last line.  Previous lines may contain GPG signature
1274    # information.
1275    date = date.splitlines()[-1]
1276    pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)
1277
1278    return pieces
1279
1280
1281def do_vcs_install(manifest_in, versionfile_source, ipy):
1282    """Git-specific installation logic for Versioneer.
1283
1284    For Git, this means creating/changing .gitattributes to mark _version.py
1285    for export-subst keyword substitution.
1286    """
1287    GITS = ["git"]
1288    if sys.platform == "win32":
1289        GITS = ["git.cmd", "git.exe"]
1290    files = [manifest_in, versionfile_source]
1291    if ipy:
1292        files.append(ipy)
1293    try:
1294        my_path = __file__
1295        if my_path.endswith(".pyc") or my_path.endswith(".pyo"):
1296            my_path = os.path.splitext(my_path)[0] + ".py"
1297        versioneer_file = os.path.relpath(my_path)
1298    except NameError:
1299        versioneer_file = "versioneer.py"
1300    files.append(versioneer_file)
1301    present = False
1302    try:
1303        with open(".gitattributes", "r") as fobj:
1304            for line in fobj:
1305                if line.strip().startswith(versionfile_source):
1306                    if "export-subst" in line.strip().split()[1:]:
1307                        present = True
1308                        break
1309    except EnvironmentError:
1310        pass
1311    if not present:
1312        with open(".gitattributes", "a+") as fobj:
1313            fobj.write(f"{versionfile_source} export-subst\n")
1314        files.append(".gitattributes")
1315    run_command(GITS, ["add", "--"] + files)
1316
1317
1318def versions_from_parentdir(parentdir_prefix, root, verbose):
1319    """Try to determine the version from the parent directory name.
1320
1321    Source tarballs conventionally unpack into a directory that includes both
1322    the project name and a version string. We will also support searching up
1323    two directory levels for an appropriately named parent directory
1324    """
1325    rootdirs = []
1326
1327    for _ in range(3):
1328        dirname = os.path.basename(root)
1329        if dirname.startswith(parentdir_prefix):
1330            return {
1331                "version": dirname[len(parentdir_prefix) :],
1332                "full-revisionid": None,
1333                "dirty": False,
1334                "error": None,
1335                "date": None,
1336            }
1337        rootdirs.append(root)
1338        root = os.path.dirname(root)  # up a level
1339
1340    if verbose:
1341        print(
1342            "Tried directories %s but none started with prefix %s"
1343            % (str(rootdirs), parentdir_prefix)
1344        )
1345    raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
1346
1347
1348SHORT_VERSION_PY = """
1349# This file was generated by 'versioneer.py' (0.20) from
1350# revision-control system data, or from the parent directory name of an
1351# unpacked source archive. Distribution tarballs contain a pre-generated copy
1352# of this file.
1353
1354import json
1355
1356version_json = '''
1357%s
1358'''  # END VERSION_JSON
1359
1360
1361def get_versions():
1362    return json.loads(version_json)
1363"""
1364
1365
1366def versions_from_file(filename):
1367    """Try to determine the version from _version.py if present."""
1368    try:
1369        with open(filename) as f:
1370            contents = f.read()
1371    except EnvironmentError:
1372        raise NotThisMethod("unable to read _version.py")
1373    mo = re.search(
1374        r"version_json = '''\n(.*)'''  # END VERSION_JSON", contents, re.M | re.S
1375    )
1376    if not mo:
1377        mo = re.search(
1378            r"version_json = '''\r\n(.*)'''  # END VERSION_JSON", contents, re.M | re.S
1379        )
1380    if not mo:
1381        raise NotThisMethod("no version_json in _version.py")
1382    return json.loads(mo.group(1))
1383
1384
1385def write_to_version_file(filename, versions):
1386    """Write the given version number to the given _version.py file."""
1387    os.unlink(filename)
1388    contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": "))
1389    with open(filename, "w") as f:
1390        f.write(SHORT_VERSION_PY % contents)
1391
1392    print("set %s to '%s'" % (filename, versions["version"]))
1393
1394
1395def plus_or_dot(pieces):
1396    """Return a + if we don't already have one, else return a ."""
1397    if "+" in pieces.get("closest-tag", ""):
1398        return "."
1399    return "+"
1400
1401
1402def render_pep440(pieces):
1403    """Build up version string, with post-release "local version identifier".
1404
1405    Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
1406    get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
1407
1408    Exceptions:
1409    1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
1410    """
1411    if pieces["closest-tag"]:
1412        rendered = pieces["closest-tag"]
1413        if pieces["distance"] or pieces["dirty"]:
1414            rendered += plus_or_dot(pieces)
1415            rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
1416            if pieces["dirty"]:
1417                rendered += ".dirty"
1418    else:
1419        # exception #1
1420        rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
1421        if pieces["dirty"]:
1422            rendered += ".dirty"
1423    return rendered
1424
1425
1426def render_pep440_branch(pieces):
1427    """TAG[[.dev0]+DISTANCE.gHEX[.dirty]] .
1428
1429    The ".dev0" means not master branch. Note that .dev0 sorts backwards
1430    (a feature branch will appear "older" than the master branch).
1431
1432    Exceptions:
1433    1: no tags. 0[.dev0]+untagged.DISTANCE.gHEX[.dirty]
1434    """
1435    if pieces["closest-tag"]:
1436        rendered = pieces["closest-tag"]
1437        if pieces["distance"] or pieces["dirty"]:
1438            if pieces["branch"] != "master":
1439                rendered += ".dev0"
1440            rendered += plus_or_dot(pieces)
1441            rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
1442            if pieces["dirty"]:
1443                rendered += ".dirty"
1444    else:
1445        # exception #1
1446        rendered = "0"
1447        if pieces["branch"] != "master":
1448            rendered += ".dev0"
1449        rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
1450        if pieces["dirty"]:
1451            rendered += ".dirty"
1452    return rendered
1453
1454
1455def render_pep440_pre(pieces):
1456    """TAG[.post0.devDISTANCE] -- No -dirty.
1457
1458    Exceptions:
1459    1: no tags. 0.post0.devDISTANCE
1460    """
1461    if pieces["closest-tag"]:
1462        rendered = pieces["closest-tag"]
1463        if pieces["distance"]:
1464            rendered += ".post0.dev%d" % pieces["distance"]
1465    else:
1466        # exception #1
1467        rendered = "0.post0.dev%d" % pieces["distance"]
1468    return rendered
1469
1470
1471def render_pep440_post(pieces):
1472    """TAG[.postDISTANCE[.dev0]+gHEX] .
1473
1474    The ".dev0" means dirty. Note that .dev0 sorts backwards
1475    (a dirty tree will appear "older" than the corresponding clean one),
1476    but you shouldn't be releasing software with -dirty anyways.
1477
1478    Exceptions:
1479    1: no tags. 0.postDISTANCE[.dev0]
1480    """
1481    if pieces["closest-tag"]:
1482        rendered = pieces["closest-tag"]
1483        if pieces["distance"] or pieces["dirty"]:
1484            rendered += ".post%d" % pieces["distance"]
1485            if pieces["dirty"]:
1486                rendered += ".dev0"
1487            rendered += plus_or_dot(pieces)
1488            rendered += "g%s" % pieces["short"]
1489    else:
1490        # exception #1
1491        rendered = "0.post%d" % pieces["distance"]
1492        if pieces["dirty"]:
1493            rendered += ".dev0"
1494        rendered += "+g%s" % pieces["short"]
1495    return rendered
1496
1497
1498def render_pep440_post_branch(pieces):
1499    """TAG[.postDISTANCE[.dev0]+gHEX[.dirty]] .
1500
1501    The ".dev0" means not master branch.
1502
1503    Exceptions:
1504    1: no tags. 0.postDISTANCE[.dev0]+gHEX[.dirty]
1505    """
1506    if pieces["closest-tag"]:
1507        rendered = pieces["closest-tag"]
1508        if pieces["distance"] or pieces["dirty"]:
1509            rendered += ".post%d" % pieces["distance"]
1510            if pieces["branch"] != "master":
1511                rendered += ".dev0"
1512            rendered += plus_or_dot(pieces)
1513            rendered += "g%s" % pieces["short"]
1514            if pieces["dirty"]:
1515                rendered += ".dirty"
1516    else:
1517        # exception #1
1518        rendered = "0.post%d" % pieces["distance"]
1519        if pieces["branch"] != "master":
1520            rendered += ".dev0"
1521        rendered += "+g%s" % pieces["short"]
1522        if pieces["dirty"]:
1523            rendered += ".dirty"
1524    return rendered
1525
1526
1527def render_pep440_old(pieces):
1528    """TAG[.postDISTANCE[.dev0]] .
1529
1530    The ".dev0" means dirty.
1531
1532    Exceptions:
1533    1: no tags. 0.postDISTANCE[.dev0]
1534    """
1535    if pieces["closest-tag"]:
1536        rendered = pieces["closest-tag"]
1537        if pieces["distance"] or pieces["dirty"]:
1538            rendered += ".post%d" % pieces["distance"]
1539            if pieces["dirty"]:
1540                rendered += ".dev0"
1541    else:
1542        # exception #1
1543        rendered = "0.post%d" % pieces["distance"]
1544        if pieces["dirty"]:
1545            rendered += ".dev0"
1546    return rendered
1547
1548
1549def render_git_describe(pieces):
1550    """TAG[-DISTANCE-gHEX][-dirty].
1551
1552    Like 'git describe --tags --dirty --always'.
1553
1554    Exceptions:
1555    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
1556    """
1557    if pieces["closest-tag"]:
1558        rendered = pieces["closest-tag"]
1559        if pieces["distance"]:
1560            rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
1561    else:
1562        # exception #1
1563        rendered = pieces["short"]
1564    if pieces["dirty"]:
1565        rendered += "-dirty"
1566    return rendered
1567
1568
1569def render_git_describe_long(pieces):
1570    """TAG-DISTANCE-gHEX[-dirty].
1571
1572    Like 'git describe --tags --dirty --always -long'.
1573    The distance/hash is unconditional.
1574
1575    Exceptions:
1576    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
1577    """
1578    if pieces["closest-tag"]:
1579        rendered = pieces["closest-tag"]
1580        rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
1581    else:
1582        # exception #1
1583        rendered = pieces["short"]
1584    if pieces["dirty"]:
1585        rendered += "-dirty"
1586    return rendered
1587
1588
1589def render(pieces, style):
1590    """Render the given version pieces into the requested style."""
1591    if pieces["error"]:
1592        return {
1593            "version": "unknown",
1594            "full-revisionid": pieces.get("long"),
1595            "dirty": None,
1596            "error": pieces["error"],
1597            "date": None,
1598        }
1599
1600    if not style or style == "default":
1601        style = "pep440"  # the default
1602
1603    if style == "pep440":
1604        rendered = render_pep440(pieces)
1605    elif style == "pep440-branch":
1606        rendered = render_pep440_branch(pieces)
1607    elif style == "pep440-pre":
1608        rendered = render_pep440_pre(pieces)
1609    elif style == "pep440-post":
1610        rendered = render_pep440_post(pieces)
1611    elif style == "pep440-post-branch":
1612        rendered = render_pep440_post_branch(pieces)
1613    elif style == "pep440-old":
1614        rendered = render_pep440_old(pieces)
1615    elif style == "git-describe":
1616        rendered = render_git_describe(pieces)
1617    elif style == "git-describe-long":
1618        rendered = render_git_describe_long(pieces)
1619    else:
1620        raise ValueError("unknown style '%s'" % style)
1621
1622    return {
1623        "version": rendered,
1624        "full-revisionid": pieces["long"],
1625        "dirty": pieces["dirty"],
1626        "error": None,
1627        "date": pieces.get("date"),
1628    }
1629
1630
1631class VersioneerBadRootError(Exception):
1632    """The project root directory is unknown or missing key files."""
1633
1634
1635def get_versions(verbose=False):
1636    """Get the project version from whatever source is available.
1637
1638    Returns dict with two keys: 'version' and 'full'.
1639    """
1640    if "versioneer" in sys.modules:
1641        # see the discussion in cmdclass.py:get_cmdclass()
1642        del sys.modules["versioneer"]
1643
1644    root = get_root()
1645    cfg = get_config_from_root(root)
1646
1647    assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg"
1648    handlers = HANDLERS.get(cfg.VCS)
1649    assert handlers, "unrecognized VCS '%s'" % cfg.VCS
1650    verbose = verbose or cfg.verbose
1651    assert (
1652        cfg.versionfile_source is not None
1653    ), "please set versioneer.versionfile_source"
1654    assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix"
1655
1656    versionfile_abs = os.path.join(root, cfg.versionfile_source)
1657
1658    # extract version from first of: _version.py, VCS command (e.g. 'git
1659    # describe'), parentdir. This is meant to work for developers using a
1660    # source checkout, for users of a tarball created by 'setup.py sdist',
1661    # and for users of a tarball/zipball created by 'git archive' or github's
1662    # download-from-tag feature or the equivalent in other VCSes.
1663
1664    get_keywords_f = handlers.get("get_keywords")
1665    from_keywords_f = handlers.get("keywords")
1666    if get_keywords_f and from_keywords_f:
1667        try:
1668            keywords = get_keywords_f(versionfile_abs)
1669            ver = from_keywords_f(keywords, cfg.tag_prefix, verbose)
1670            if verbose:
1671                print("got version from expanded keyword %s" % ver)
1672            return ver
1673        except NotThisMethod:
1674            pass
1675
1676    try:
1677        ver = versions_from_file(versionfile_abs)
1678        if verbose:
1679            print("got version from file %s %s" % (versionfile_abs, ver))
1680        return ver
1681    except NotThisMethod:
1682        pass
1683
1684    from_vcs_f = handlers.get("pieces_from_vcs")
1685    if from_vcs_f:
1686        try:
1687            pieces = from_vcs_f(cfg.tag_prefix, root, verbose)
1688            ver = render(pieces, cfg.style)
1689            if verbose:
1690                print("got version from VCS %s" % ver)
1691            return ver
1692        except NotThisMethod:
1693            pass
1694
1695    try:
1696        if cfg.parentdir_prefix:
1697            ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
1698            if verbose:
1699                print("got version from parentdir %s" % ver)
1700            return ver
1701    except NotThisMethod:
1702        pass
1703
1704    if verbose:
1705        print("unable to compute version")
1706
1707    return {
1708        "version": "0+unknown",
1709        "full-revisionid": None,
1710        "dirty": None,
1711        "error": "unable to compute version",
1712        "date": None,
1713    }
1714
1715
1716def get_version():
1717    """Get the short version string for this project."""
1718    return get_versions()["version"]
1719
1720
1721def get_cmdclass(cmdclass=None):
1722    """Get the custom setuptools/distutils subclasses used by Versioneer.
1723
1724    If the package uses a different cmdclass (e.g. one from numpy), it
1725    should be provide as an argument.
1726    """
1727    if "versioneer" in sys.modules:
1728        del sys.modules["versioneer"]
1729        # this fixes the "python setup.py develop" case (also 'install' and
1730        # 'easy_install .'), in which subdependencies of the main project are
1731        # built (using setup.py bdist_egg) in the same python process. Assume
1732        # a main project A and a dependency B, which use different versions
1733        # of Versioneer. A's setup.py imports A's Versioneer, leaving it in
1734        # sys.modules by the time B's setup.py is executed, causing B to run
1735        # with the wrong versioneer. Setuptools wraps the sub-dep builds in a
1736        # sandbox that restores sys.modules to it's pre-build state, so the
1737        # parent is protected against the child's "import versioneer". By
1738        # removing ourselves from sys.modules here, before the child build
1739        # happens, we protect the child from the parent's versioneer too.
1740        # Also see https://github.com/python-versioneer/python-versioneer/issues/52
1741
1742    cmds = {} if cmdclass is None else cmdclass.copy()
1743
1744    # we add "version" to both distutils and setuptools
1745    from distutils.core import Command
1746
1747    class cmd_version(Command):
1748        description = "report generated version string"
1749        user_options = []
1750        boolean_options = []
1751
1752        def initialize_options(self):
1753            pass
1754
1755        def finalize_options(self):
1756            pass
1757
1758        def run(self):
1759            vers = get_versions(verbose=True)
1760            print("Version: %s" % vers["version"])
1761            print(" full-revisionid: %s" % vers.get("full-revisionid"))
1762            print(" dirty: %s" % vers.get("dirty"))
1763            print(" date: %s" % vers.get("date"))
1764            if vers["error"]:
1765                print(" error: %s" % vers["error"])
1766
1767    cmds["version"] = cmd_version
1768
1769    # we override "build_py" in both distutils and setuptools
1770    #
1771    # most invocation pathways end up running build_py:
1772    #  distutils/build -> build_py
1773    #  distutils/install -> distutils/build ->..
1774    #  setuptools/bdist_wheel -> distutils/install ->..
1775    #  setuptools/bdist_egg -> distutils/install_lib -> build_py
1776    #  setuptools/install -> bdist_egg ->..
1777    #  setuptools/develop -> ?
1778    #  pip install:
1779    #   copies source tree to a tempdir before running egg_info/etc
1780    #   if .git isn't copied too, 'git describe' will fail
1781    #   then does setup.py bdist_wheel, or sometimes setup.py install
1782    #  setup.py egg_info -> ?
1783
1784    # we override different "build_py" commands for both environments
1785    if "build_py" in cmds:
1786        _build_py = cmds["build_py"]
1787    elif "setuptools" in sys.modules:
1788        from setuptools.command.build_py import build_py as _build_py
1789    else:
1790        from distutils.command.build_py import build_py as _build_py
1791
1792    class cmd_build_py(_build_py):
1793        def run(self):
1794            root = get_root()
1795            cfg = get_config_from_root(root)
1796            versions = get_versions()
1797            _build_py.run(self)
1798            # now locate _version.py in the new build/ directory and replace
1799            # it with an updated value
1800            if cfg.versionfile_build:
1801                target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build)
1802                print("UPDATING %s" % target_versionfile)
1803                write_to_version_file(target_versionfile, versions)
1804
1805    cmds["build_py"] = cmd_build_py
1806
1807    if "build_ext" in cmds:
1808        _build_ext = cmds["build_ext"]
1809    elif "setuptools" in sys.modules:
1810        from setuptools.command.build_ext import build_ext as _build_ext
1811    else:
1812        from distutils.command.build_ext import build_ext as _build_ext
1813
1814    class cmd_build_ext(_build_ext):
1815        def run(self):
1816            root = get_root()
1817            cfg = get_config_from_root(root)
1818            versions = get_versions()
1819            _build_ext.run(self)
1820            if self.inplace:
1821                # build_ext --inplace will only build extensions in
1822                # build/lib<..> dir with no _version.py to write to.
1823                # As in place builds will already have a _version.py
1824                # in the module dir, we do not need to write one.
1825                return
1826            # now locate _version.py in the new build/ directory and replace
1827            # it with an updated value
1828            target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build)
1829            print("UPDATING %s" % target_versionfile)
1830            write_to_version_file(target_versionfile, versions)
1831
1832    cmds["build_ext"] = cmd_build_ext
1833
1834    if "cx_Freeze" in sys.modules:  # cx_freeze enabled?
1835        from cx_Freeze.dist import build_exe as _build_exe
1836
1837        # nczeczulin reports that py2exe won't like the pep440-style string
1838        # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g.
1839        # setup(console=[{
1840        #   "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION
1841        #   "product_version": versioneer.get_version(),
1842        #   ...
1843
1844        class cmd_build_exe(_build_exe):
1845            def run(self):
1846                root = get_root()
1847                cfg = get_config_from_root(root)
1848                versions = get_versions()
1849                target_versionfile = cfg.versionfile_source
1850                print("UPDATING %s" % target_versionfile)
1851                write_to_version_file(target_versionfile, versions)
1852
1853                _build_exe.run(self)
1854                os.unlink(target_versionfile)
1855                with open(cfg.versionfile_source, "w") as f:
1856                    LONG = LONG_VERSION_PY[cfg.VCS]
1857                    f.write(
1858                        LONG
1859                        % {
1860                            "DOLLAR": "$",
1861                            "STYLE": cfg.style,
1862                            "TAG_PREFIX": cfg.tag_prefix,
1863                            "PARENTDIR_PREFIX": cfg.parentdir_prefix,
1864                            "VERSIONFILE_SOURCE": cfg.versionfile_source,
1865                        }
1866                    )
1867
1868        cmds["build_exe"] = cmd_build_exe
1869        del cmds["build_py"]
1870
1871    if "py2exe" in sys.modules:  # py2exe enabled?
1872        from py2exe.distutils_buildexe import py2exe as _py2exe
1873
1874        class cmd_py2exe(_py2exe):
1875            def run(self):
1876                root = get_root()
1877                cfg = get_config_from_root(root)
1878                versions = get_versions()
1879                target_versionfile = cfg.versionfile_source
1880                print("UPDATING %s" % target_versionfile)
1881                write_to_version_file(target_versionfile, versions)
1882
1883                _py2exe.run(self)
1884                os.unlink(target_versionfile)
1885                with open(cfg.versionfile_source, "w") as f:
1886                    LONG = LONG_VERSION_PY[cfg.VCS]
1887                    f.write(
1888                        LONG
1889                        % {
1890                            "DOLLAR": "$",
1891                            "STYLE": cfg.style,
1892                            "TAG_PREFIX": cfg.tag_prefix,
1893                            "PARENTDIR_PREFIX": cfg.parentdir_prefix,
1894                            "VERSIONFILE_SOURCE": cfg.versionfile_source,
1895                        }
1896                    )
1897
1898        cmds["py2exe"] = cmd_py2exe
1899
1900    # we override different "sdist" commands for both environments
1901    if "sdist" in cmds:
1902        _sdist = cmds["sdist"]
1903    elif "setuptools" in sys.modules:
1904        from setuptools.command.sdist import sdist as _sdist
1905    else:
1906        from distutils.command.sdist import sdist as _sdist
1907
1908    class cmd_sdist(_sdist):
1909        def run(self):
1910            versions = get_versions()
1911            # pylint:disable=attribute-defined-outside-init # noqa
1912            self._versioneer_generated_versions = versions
1913            # unless we update this, the command will keep using the old
1914            # version
1915            self.distribution.metadata.version = versions["version"]
1916            return _sdist.run(self)
1917
1918        def make_release_tree(self, base_dir, files):
1919            root = get_root()
1920            cfg = get_config_from_root(root)
1921            _sdist.make_release_tree(self, base_dir, files)
1922            # now locate _version.py in the new base_dir directory
1923            # (remembering that it may be a hardlink) and replace it with an
1924            # updated value
1925            target_versionfile = os.path.join(base_dir, cfg.versionfile_source)
1926            print("UPDATING %s" % target_versionfile)
1927            write_to_version_file(
1928                target_versionfile, self._versioneer_generated_versions
1929            )
1930
1931    cmds["sdist"] = cmd_sdist
1932
1933    return cmds
1934
1935
1936CONFIG_ERROR = """
1937setup.cfg is missing the necessary Versioneer configuration. You need
1938a section like:
1939
1940 [versioneer]
1941 VCS = git
1942 style = pep440
1943 versionfile_source = src/myproject/_version.py
1944 versionfile_build = myproject/_version.py
1945 tag_prefix =
1946 parentdir_prefix = myproject-
1947
1948You will also need to edit your setup.py to use the results:
1949
1950 import versioneer
1951 setup(version=versioneer.get_version(),
1952       cmdclass=versioneer.get_cmdclass(), ...)
1953
1954Please read the docstring in ./versioneer.py for configuration instructions,
1955edit setup.cfg, and re-run the installer or 'python versioneer.py setup'.
1956"""
1957
1958SAMPLE_CONFIG = """
1959# See the docstring in versioneer.py for instructions. Note that you must
1960# re-run 'versioneer.py setup' after changing this section, and commit the
1961# resulting files.
1962
1963[versioneer]
1964#VCS = git
1965#style = pep440
1966#versionfile_source =
1967#versionfile_build =
1968#tag_prefix =
1969#parentdir_prefix =
1970
1971"""
1972
1973OLD_SNIPPET = """
1974from ._version import get_versions
1975__version__ = get_versions()['version']
1976del get_versions
1977"""
1978
1979INIT_PY_SNIPPET = """
1980from . import {0}
1981__version__ = {0}.get_versions()['version']
1982"""
1983
1984
1985def do_setup():
1986    """Do main VCS-independent setup function for installing Versioneer."""
1987    root = get_root()
1988    try:
1989        cfg = get_config_from_root(root)
1990    except (
1991        EnvironmentError,
1992        configparser.NoSectionError,
1993        configparser.NoOptionError,
1994    ) as e:
1995        if isinstance(e, (EnvironmentError, configparser.NoSectionError)):
1996            print("Adding sample versioneer config to setup.cfg", file=sys.stderr)
1997            with open(os.path.join(root, "setup.cfg"), "a") as f:
1998                f.write(SAMPLE_CONFIG)
1999        print(CONFIG_ERROR, file=sys.stderr)
2000        return 1
2001
2002    print(" creating %s" % cfg.versionfile_source)
2003    with open(cfg.versionfile_source, "w") as f:
2004        LONG = LONG_VERSION_PY[cfg.VCS]
2005        f.write(
2006            LONG
2007            % {
2008                "DOLLAR": "$",
2009                "STYLE": cfg.style,
2010                "TAG_PREFIX": cfg.tag_prefix,
2011                "PARENTDIR_PREFIX": cfg.parentdir_prefix,
2012                "VERSIONFILE_SOURCE": cfg.versionfile_source,
2013            }
2014        )
2015
2016    ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py")
2017    if os.path.exists(ipy):
2018        try:
2019            with open(ipy, "r") as f:
2020                old = f.read()
2021        except EnvironmentError:
2022            old = ""
2023        module = os.path.splitext(os.path.basename(cfg.versionfile_source))[0]
2024        snippet = INIT_PY_SNIPPET.format(module)
2025        if OLD_SNIPPET in old:
2026            print(" replacing boilerplate in %s" % ipy)
2027            with open(ipy, "w") as f:
2028                f.write(old.replace(OLD_SNIPPET, snippet))
2029        elif snippet not in old:
2030            print(" appending to %s" % ipy)
2031            with open(ipy, "a") as f:
2032                f.write(snippet)
2033        else:
2034            print(" %s unmodified" % ipy)
2035    else:
2036        print(" %s doesn't exist, ok" % ipy)
2037        ipy = None
2038
2039    # Make sure both the top-level "versioneer.py" and versionfile_source
2040    # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so
2041    # they'll be copied into source distributions. Pip won't be able to
2042    # install the package without this.
2043    manifest_in = os.path.join(root, "MANIFEST.in")
2044    simple_includes = set()
2045    try:
2046        with open(manifest_in, "r") as f:
2047            for line in f:
2048                if line.startswith("include "):
2049                    for include in line.split()[1:]:
2050                        simple_includes.add(include)
2051    except EnvironmentError:
2052        pass
2053    # That doesn't cover everything MANIFEST.in can do
2054    # (http://docs.python.org/2/distutils/sourcedist.html#commands), so
2055    # it might give some false negatives. Appending redundant 'include'
2056    # lines is safe, though.
2057    if "versioneer.py" not in simple_includes:
2058        print(" appending 'versioneer.py' to MANIFEST.in")
2059        with open(manifest_in, "a") as f:
2060            f.write("include versioneer.py\n")
2061    else:
2062        print(" 'versioneer.py' already in MANIFEST.in")
2063    if cfg.versionfile_source not in simple_includes:
2064        print(
2065            " appending versionfile_source ('%s') to MANIFEST.in"
2066            % cfg.versionfile_source
2067        )
2068        with open(manifest_in, "a") as f:
2069            f.write("include %s\n" % cfg.versionfile_source)
2070    else:
2071        print(" versionfile_source already in MANIFEST.in")
2072
2073    # Make VCS-specific changes. For git, this means creating/changing
2074    # .gitattributes to mark _version.py for export-subst keyword
2075    # substitution.
2076    do_vcs_install(manifest_in, cfg.versionfile_source, ipy)
2077    return 0
2078
2079
2080def scan_setup_py():
2081    """Validate the contents of setup.py against Versioneer's expectations."""
2082    found = set()
2083    setters = False
2084    errors = 0
2085    with open("setup.py", "r") as f:
2086        for line in f.readlines():
2087            if "import versioneer" in line:
2088                found.add("import")
2089            if "versioneer.get_cmdclass()" in line:
2090                found.add("cmdclass")
2091            if "versioneer.get_version()" in line:
2092                found.add("get_version")
2093            if "versioneer.VCS" in line:
2094                setters = True
2095            if "versioneer.versionfile_source" in line:
2096                setters = True
2097    if len(found) != 3:
2098        print("")
2099        print("Your setup.py appears to be missing some important items")
2100        print("(but I might be wrong). Please make sure it has something")
2101        print("roughly like the following:")
2102        print("")
2103        print(" import versioneer")
2104        print(" setup( version=versioneer.get_version(),")
2105        print("        cmdclass=versioneer.get_cmdclass(),  ...)")
2106        print("")
2107        errors += 1
2108    if setters:
2109        print("You should remove lines like 'versioneer.VCS = ' and")
2110        print("'versioneer.versionfile_source = ' . This configuration")
2111        print("now lives in setup.cfg, and should be removed from setup.py")
2112        print("")
2113        errors += 1
2114    return errors
2115
2116
2117if __name__ == "__main__":
2118    cmd = sys.argv[1]
2119    if cmd == "setup":
2120        errors = do_setup()
2121        errors += scan_setup_py()
2122        if errors:
2123            sys.exit(1)
2124