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