]> git.madduck.net Git - etc/vim.git/blob - versioneer.py

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

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