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