1 2# Version: 0.15 +matplotlib modifications to avoid the need for setup.cfg to exist. 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 400 401 parser = configparser.SafeConfigParser() 402 if os.path.exists(setup_cfg): 403 with open(setup_cfg, "r") as f: 404 parser.readfp(f) 405 406 def get(parser, name): 407 if parser.has_option("versioneer", name): 408 return parser.get("versioneer", name) 409 return None 410 cfg = VersioneerConfig() 411 412 VCS = get(parser, "VCS") or 'git' 413 cfg.VCS = VCS 414 cfg.style = get(parser, "style") or "pep440" 415 version_file = os.path.join('lib', 'matplotlib', '_version.py') 416 cfg.versionfile_source = get(parser, "versionfile_source") or version_file 417 cfg.versionfile_build = get(parser, "versionfile_build") or os.path.join('matplotlib', '_version.py') 418 cfg.tag_prefix = get(parser, "tag_prefix") or 'v' 419 cfg.parentdir_prefix = get(parser, "parentdir_prefix") or 'matplotlib-' 420 cfg.verbose = get(parser, "verbose") 421 return cfg 422 423 424class NotThisMethod(Exception): 425 pass 426 427# these dictionaries contain VCS-specific tools 428LONG_VERSION_PY = {} 429HANDLERS = {} 430 431 432def register_vcs_handler(vcs, method): # decorator 433 def decorate(f): 434 if vcs not in HANDLERS: 435 HANDLERS[vcs] = {} 436 HANDLERS[vcs][method] = f 437 return f 438 return decorate 439 440 441def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): 442 assert isinstance(commands, list) 443 p = None 444 for c in commands: 445 try: 446 dispcmd = str([c] + args) 447 # remember shell=False, so use git.cmd on windows, not just git 448 p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE, 449 stderr=(subprocess.PIPE if hide_stderr 450 else None)) 451 break 452 except EnvironmentError: 453 e = sys.exc_info()[1] 454 if e.errno == errno.ENOENT: 455 continue 456 if verbose: 457 print("unable to run %s" % dispcmd) 458 print(e) 459 return None 460 else: 461 if verbose: 462 print("unable to find command, tried %s" % (commands,)) 463 return None 464 stdout = p.communicate()[0].strip() 465 if sys.version_info[0] >= 3: 466 stdout = stdout.decode() 467 if p.returncode != 0: 468 if verbose: 469 print("unable to run %s (error)" % dispcmd) 470 return None 471 return stdout 472LONG_VERSION_PY['git'] = r''' 473# This file helps to compute a version number in source trees obtained from 474# git-archive tarball (such as those provided by githubs download-from-tag 475# feature). Distribution tarballs (built by setup.py sdist) and build 476# directories (produced by setup.py build) will contain a much shorter file 477# that just contains the computed version number. 478 479# This file is released into the public domain. Generated by 480# versioneer-0.15 (https://github.com/warner/python-versioneer) 481 482import errno 483import os 484import re 485import subprocess 486import sys 487 488 489def get_keywords(): 490 # these strings will be replaced by git during git-archive. 491 # setup.py/versioneer.py will grep for the variable names, so they must 492 # each be defined on a line of their own. _version.py will just call 493 # get_keywords(). 494 git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" 495 git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" 496 keywords = {"refnames": git_refnames, "full": git_full} 497 return keywords 498 499 500class VersioneerConfig: 501 pass 502 503 504def get_config(): 505 # these strings are filled in when 'setup.py versioneer' creates 506 # _version.py 507 cfg = VersioneerConfig() 508 cfg.VCS = "git" 509 cfg.style = "%(STYLE)s" 510 cfg.tag_prefix = "%(TAG_PREFIX)s" 511 cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" 512 cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" 513 cfg.verbose = False 514 return cfg 515 516 517class NotThisMethod(Exception): 518 pass 519 520 521LONG_VERSION_PY = {} 522HANDLERS = {} 523 524 525def register_vcs_handler(vcs, method): # decorator 526 def decorate(f): 527 if vcs not in HANDLERS: 528 HANDLERS[vcs] = {} 529 HANDLERS[vcs][method] = f 530 return f 531 return decorate 532 533 534def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False): 535 assert isinstance(commands, list) 536 p = None 537 for c in commands: 538 try: 539 dispcmd = str([c] + args) 540 # remember shell=False, so use git.cmd on windows, not just git 541 p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE, 542 stderr=(subprocess.PIPE if hide_stderr 543 else None)) 544 break 545 except EnvironmentError: 546 e = sys.exc_info()[1] 547 if e.errno == errno.ENOENT: 548 continue 549 if verbose: 550 print("unable to run %%s" %% dispcmd) 551 print(e) 552 return None 553 else: 554 if verbose: 555 print("unable to find command, tried %%s" %% (commands,)) 556 return None 557 stdout = p.communicate()[0].strip() 558 if sys.version_info[0] >= 3: 559 stdout = stdout.decode() 560 if p.returncode != 0: 561 if verbose: 562 print("unable to run %%s (error)" %% dispcmd) 563 return None 564 return stdout 565 566 567def versions_from_parentdir(parentdir_prefix, root, verbose): 568 # Source tarballs conventionally unpack into a directory that includes 569 # both the project name and a version string. 570 dirname = os.path.basename(root) 571 if not dirname.startswith(parentdir_prefix): 572 if verbose: 573 print("guessing rootdir is '%%s', but '%%s' doesn't start with " 574 "prefix '%%s'" %% (root, dirname, parentdir_prefix)) 575 raise NotThisMethod("rootdir doesn't start with parentdir_prefix") 576 return {"version": dirname[len(parentdir_prefix):], 577 "full-revisionid": None, 578 "dirty": False, "error": None} 579 580 581@register_vcs_handler("git", "get_keywords") 582def git_get_keywords(versionfile_abs): 583 # the code embedded in _version.py can just fetch the value of these 584 # keywords. When used from setup.py, we don't want to import _version.py, 585 # so we do it with a regexp instead. This function is not used from 586 # _version.py. 587 keywords = {} 588 try: 589 f = open(versionfile_abs, "r") 590 for line in f.readlines(): 591 if line.strip().startswith("git_refnames ="): 592 mo = re.search(r'=\s*"(.*)"', line) 593 if mo: 594 keywords["refnames"] = mo.group(1) 595 if line.strip().startswith("git_full ="): 596 mo = re.search(r'=\s*"(.*)"', line) 597 if mo: 598 keywords["full"] = mo.group(1) 599 f.close() 600 except EnvironmentError: 601 pass 602 return keywords 603 604 605@register_vcs_handler("git", "keywords") 606def git_versions_from_keywords(keywords, tag_prefix, verbose): 607 if not keywords: 608 raise NotThisMethod("no keywords at all, weird") 609 refnames = keywords["refnames"].strip() 610 if refnames.startswith("$Format"): 611 if verbose: 612 print("keywords are unexpanded, not using") 613 raise NotThisMethod("unexpanded keywords, not a git-archive tarball") 614 refs = set([r.strip() for r in refnames.strip("()").split(",")]) 615 # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of 616 # just "foo-1.0". If we see a "tag: " prefix, prefer those. 617 TAG = "tag: " 618 tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) 619 if not tags: 620 # Either we're using git < 1.8.3, or there really are no tags. We use 621 # a heuristic: assume all version tags have a digit. The old git %%d 622 # expansion behaves like git log --decorate=short and strips out the 623 # refs/heads/ and refs/tags/ prefixes that would let us distinguish 624 # between branches and tags. By ignoring refnames without digits, we 625 # filter out many common branch names like "release" and 626 # "stabilization", as well as "HEAD" and "master". 627 tags = set([r for r in refs if re.search(r'\d', r)]) 628 if verbose: 629 print("discarding '%%s', no digits" %% ",".join(refs-tags)) 630 if verbose: 631 print("likely tags: %%s" %% ",".join(sorted(tags))) 632 for ref in sorted(tags): 633 # sorting will prefer e.g. "2.0" over "2.0rc1" 634 if ref.startswith(tag_prefix): 635 r = ref[len(tag_prefix):] 636 if verbose: 637 print("picking %%s" %% r) 638 return {"version": r, 639 "full-revisionid": keywords["full"].strip(), 640 "dirty": False, "error": None 641 } 642 # no suitable tags, so version is "0+unknown", but full hex is still there 643 if verbose: 644 print("no suitable tags, using unknown + full revision id") 645 return {"version": "0+unknown", 646 "full-revisionid": keywords["full"].strip(), 647 "dirty": False, "error": "no suitable tags"} 648 649 650@register_vcs_handler("git", "pieces_from_vcs") 651def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): 652 # this runs 'git' from the root of the source tree. This only gets called 653 # if the git-archive 'subst' keywords were *not* expanded, and 654 # _version.py hasn't already been rewritten with a short version string, 655 # meaning we're inside a checked out source tree. 656 657 if not os.path.exists(os.path.join(root, ".git")): 658 if verbose: 659 print("no .git in %%s" %% root) 660 raise NotThisMethod("no .git directory") 661 662 GITS = ["git"] 663 if sys.platform == "win32": 664 GITS = ["git.cmd", "git.exe"] 665 # if there is a tag, this yields TAG-NUM-gHEX[-dirty] 666 # if there are no tags, this yields HEX[-dirty] (no NUM) 667 describe_out = run_command(GITS, ["describe", "--tags", "--dirty", 668 "--always", "--long"], 669 cwd=root) 670 # --long was added in git-1.5.5 671 if describe_out is None: 672 raise NotThisMethod("'git describe' failed") 673 describe_out = describe_out.strip() 674 full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) 675 if full_out is None: 676 raise NotThisMethod("'git rev-parse' failed") 677 full_out = full_out.strip() 678 679 pieces = {} 680 pieces["long"] = full_out 681 pieces["short"] = full_out[:7] # maybe improved later 682 pieces["error"] = None 683 684 # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] 685 # TAG might have hyphens. 686 git_describe = describe_out 687 688 # look for -dirty suffix 689 dirty = git_describe.endswith("-dirty") 690 pieces["dirty"] = dirty 691 if dirty: 692 git_describe = git_describe[:git_describe.rindex("-dirty")] 693 694 # now we have TAG-NUM-gHEX or HEX 695 696 if "-" in git_describe: 697 # TAG-NUM-gHEX 698 mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) 699 if not mo: 700 # unparsable. Maybe git-describe is misbehaving? 701 pieces["error"] = ("unable to parse git-describe output: '%%s'" 702 %% describe_out) 703 return pieces 704 705 # tag 706 full_tag = mo.group(1) 707 if not full_tag.startswith(tag_prefix): 708 if verbose: 709 fmt = "tag '%%s' doesn't start with prefix '%%s'" 710 print(fmt %% (full_tag, tag_prefix)) 711 pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'" 712 %% (full_tag, tag_prefix)) 713 return pieces 714 pieces["closest-tag"] = full_tag[len(tag_prefix):] 715 716 # distance: number of commits since tag 717 pieces["distance"] = int(mo.group(2)) 718 719 # commit: short hex revision ID 720 pieces["short"] = mo.group(3) 721 722 else: 723 # HEX: no tags 724 pieces["closest-tag"] = None 725 count_out = run_command(GITS, ["rev-list", "HEAD", "--count"], 726 cwd=root) 727 pieces["distance"] = int(count_out) # total number of commits 728 729 return pieces 730 731 732def plus_or_dot(pieces): 733 if "+" in pieces.get("closest-tag", ""): 734 return "." 735 return "+" 736 737 738def render_pep440(pieces): 739 # now build up version string, with post-release "local version 740 # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you 741 # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty 742 743 # exceptions: 744 # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] 745 746 if pieces["closest-tag"]: 747 rendered = pieces["closest-tag"] 748 if pieces["distance"] or pieces["dirty"]: 749 rendered += plus_or_dot(pieces) 750 rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) 751 if pieces["dirty"]: 752 rendered += ".dirty" 753 else: 754 # exception #1 755 rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], 756 pieces["short"]) 757 if pieces["dirty"]: 758 rendered += ".dirty" 759 return rendered 760 761 762def render_pep440_pre(pieces): 763 # TAG[.post.devDISTANCE] . No -dirty 764 765 # exceptions: 766 # 1: no tags. 0.post.devDISTANCE 767 768 if pieces["closest-tag"]: 769 rendered = pieces["closest-tag"] 770 if pieces["distance"]: 771 rendered += ".post.dev%%d" %% pieces["distance"] 772 else: 773 # exception #1 774 rendered = "0.post.dev%%d" %% pieces["distance"] 775 return rendered 776 777 778def render_pep440_post(pieces): 779 # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that 780 # .dev0 sorts backwards (a dirty tree will appear "older" than the 781 # corresponding clean one), but you shouldn't be releasing software with 782 # -dirty anyways. 783 784 # exceptions: 785 # 1: no tags. 0.postDISTANCE[.dev0] 786 787 if pieces["closest-tag"]: 788 rendered = pieces["closest-tag"] 789 if pieces["distance"] or pieces["dirty"]: 790 rendered += ".post%%d" %% pieces["distance"] 791 if pieces["dirty"]: 792 rendered += ".dev0" 793 rendered += plus_or_dot(pieces) 794 rendered += "g%%s" %% pieces["short"] 795 else: 796 # exception #1 797 rendered = "0.post%%d" %% pieces["distance"] 798 if pieces["dirty"]: 799 rendered += ".dev0" 800 rendered += "+g%%s" %% pieces["short"] 801 return rendered 802 803 804def render_pep440_old(pieces): 805 # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. 806 807 # exceptions: 808 # 1: no tags. 0.postDISTANCE[.dev0] 809 810 if pieces["closest-tag"]: 811 rendered = pieces["closest-tag"] 812 if pieces["distance"] or pieces["dirty"]: 813 rendered += ".post%%d" %% pieces["distance"] 814 if pieces["dirty"]: 815 rendered += ".dev0" 816 else: 817 # exception #1 818 rendered = "0.post%%d" %% pieces["distance"] 819 if pieces["dirty"]: 820 rendered += ".dev0" 821 return rendered 822 823 824def render_git_describe(pieces): 825 # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty 826 # --always' 827 828 # exceptions: 829 # 1: no tags. HEX[-dirty] (note: no 'g' prefix) 830 831 if pieces["closest-tag"]: 832 rendered = pieces["closest-tag"] 833 if pieces["distance"]: 834 rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) 835 else: 836 # exception #1 837 rendered = pieces["short"] 838 if pieces["dirty"]: 839 rendered += "-dirty" 840 return rendered 841 842 843def render_git_describe_long(pieces): 844 # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty 845 # --always -long'. The distance/hash is unconditional. 846 847 # exceptions: 848 # 1: no tags. HEX[-dirty] (note: no 'g' prefix) 849 850 if pieces["closest-tag"]: 851 rendered = pieces["closest-tag"] 852 rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) 853 else: 854 # exception #1 855 rendered = pieces["short"] 856 if pieces["dirty"]: 857 rendered += "-dirty" 858 return rendered 859 860 861def render(pieces, style): 862 if pieces["error"]: 863 return {"version": "unknown", 864 "full-revisionid": pieces.get("long"), 865 "dirty": None, 866 "error": pieces["error"]} 867 868 if not style or style == "default": 869 style = "pep440" # the default 870 871 if style == "pep440": 872 rendered = render_pep440(pieces) 873 elif style == "pep440-pre": 874 rendered = render_pep440_pre(pieces) 875 elif style == "pep440-post": 876 rendered = render_pep440_post(pieces) 877 elif style == "pep440-old": 878 rendered = render_pep440_old(pieces) 879 elif style == "git-describe": 880 rendered = render_git_describe(pieces) 881 elif style == "git-describe-long": 882 rendered = render_git_describe_long(pieces) 883 else: 884 raise ValueError("unknown style '%%s'" %% style) 885 886 return {"version": rendered, "full-revisionid": pieces["long"], 887 "dirty": pieces["dirty"], "error": None} 888 889 890def get_versions(): 891 # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have 892 # __file__, we can work backwards from there to the root. Some 893 # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which 894 # case we can only use expanded keywords. 895 896 cfg = get_config() 897 verbose = cfg.verbose 898 899 try: 900 return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, 901 verbose) 902 except NotThisMethod: 903 pass 904 905 try: 906 root = os.path.realpath(__file__) 907 # versionfile_source is the relative path from the top of the source 908 # tree (where the .git directory might live) to this file. Invert 909 # this to find the root from __file__. 910 for i in cfg.versionfile_source.split('/'): 911 root = os.path.dirname(root) 912 except NameError: 913 return {"version": "0+unknown", "full-revisionid": None, 914 "dirty": None, 915 "error": "unable to find root of source tree"} 916 917 try: 918 pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) 919 return render(pieces, cfg.style) 920 except NotThisMethod: 921 pass 922 923 try: 924 if cfg.parentdir_prefix: 925 return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) 926 except NotThisMethod: 927 pass 928 929 return {"version": "0+unknown", "full-revisionid": None, 930 "dirty": None, 931 "error": "unable to compute version"} 932''' 933 934 935@register_vcs_handler("git", "get_keywords") 936def git_get_keywords(versionfile_abs): 937 # the code embedded in _version.py can just fetch the value of these 938 # keywords. When used from setup.py, we don't want to import _version.py, 939 # so we do it with a regexp instead. This function is not used from 940 # _version.py. 941 keywords = {} 942 try: 943 f = open(versionfile_abs, "r") 944 for line in f.readlines(): 945 if line.strip().startswith("git_refnames ="): 946 mo = re.search(r'=\s*"(.*)"', line) 947 if mo: 948 keywords["refnames"] = mo.group(1) 949 if line.strip().startswith("git_full ="): 950 mo = re.search(r'=\s*"(.*)"', line) 951 if mo: 952 keywords["full"] = mo.group(1) 953 f.close() 954 except EnvironmentError: 955 pass 956 return keywords 957 958 959@register_vcs_handler("git", "keywords") 960def git_versions_from_keywords(keywords, tag_prefix, verbose): 961 if not keywords: 962 raise NotThisMethod("no keywords at all, weird") 963 refnames = keywords["refnames"].strip() 964 if refnames.startswith("$Format"): 965 if verbose: 966 print("keywords are unexpanded, not using") 967 raise NotThisMethod("unexpanded keywords, not a git-archive tarball") 968 refs = set([r.strip() for r in refnames.strip("()").split(",")]) 969 # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of 970 # just "foo-1.0". If we see a "tag: " prefix, prefer those. 971 TAG = "tag: " 972 tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) 973 if not tags: 974 # Either we're using git < 1.8.3, or there really are no tags. We use 975 # a heuristic: assume all version tags have a digit. The old git %d 976 # expansion behaves like git log --decorate=short and strips out the 977 # refs/heads/ and refs/tags/ prefixes that would let us distinguish 978 # between branches and tags. By ignoring refnames without digits, we 979 # filter out many common branch names like "release" and 980 # "stabilization", as well as "HEAD" and "master". 981 tags = set([r for r in refs if re.search(r'\d', r)]) 982 if verbose: 983 print("discarding '%s', no digits" % ",".join(refs-tags)) 984 if verbose: 985 print("likely tags: %s" % ",".join(sorted(tags))) 986 for ref in sorted(tags): 987 # sorting will prefer e.g. "2.0" over "2.0rc1" 988 if ref.startswith(tag_prefix): 989 r = ref[len(tag_prefix):] 990 if verbose: 991 print("picking %s" % r) 992 return {"version": r, 993 "full-revisionid": keywords["full"].strip(), 994 "dirty": False, "error": None 995 } 996 # no suitable tags, so version is "0+unknown", but full hex is still there 997 if verbose: 998 print("no suitable tags, using unknown + full revision id") 999 return {"version": "0+unknown", 1000 "full-revisionid": keywords["full"].strip(), 1001 "dirty": False, "error": "no suitable tags"} 1002 1003 1004@register_vcs_handler("git", "pieces_from_vcs") 1005def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): 1006 # this runs 'git' from the root of the source tree. This only gets called 1007 # if the git-archive 'subst' keywords were *not* expanded, and 1008 # _version.py hasn't already been rewritten with a short version string, 1009 # meaning we're inside a checked out source tree. 1010 1011 if not os.path.exists(os.path.join(root, ".git")): 1012 if verbose: 1013 print("no .git in %s" % root) 1014 raise NotThisMethod("no .git directory") 1015 1016 GITS = ["git"] 1017 if sys.platform == "win32": 1018 GITS = ["git.cmd", "git.exe"] 1019 # if there is a tag, this yields TAG-NUM-gHEX[-dirty] 1020 # if there are no tags, this yields HEX[-dirty] (no NUM) 1021 describe_out = run_command(GITS, ["describe", "--tags", "--dirty", 1022 "--always", "--long"], 1023 cwd=root) 1024 # --long was added in git-1.5.5 1025 if describe_out is None: 1026 raise NotThisMethod("'git describe' failed") 1027 describe_out = describe_out.strip() 1028 full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) 1029 if full_out is None: 1030 raise NotThisMethod("'git rev-parse' failed") 1031 full_out = full_out.strip() 1032 1033 pieces = {} 1034 pieces["long"] = full_out 1035 pieces["short"] = full_out[:7] # maybe improved later 1036 pieces["error"] = None 1037 1038 # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] 1039 # TAG might have hyphens. 1040 git_describe = describe_out 1041 1042 # look for -dirty suffix 1043 dirty = git_describe.endswith("-dirty") 1044 pieces["dirty"] = dirty 1045 if dirty: 1046 git_describe = git_describe[:git_describe.rindex("-dirty")] 1047 1048 # now we have TAG-NUM-gHEX or HEX 1049 1050 if "-" in git_describe: 1051 # TAG-NUM-gHEX 1052 mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) 1053 if not mo: 1054 # unparsable. Maybe git-describe is misbehaving? 1055 pieces["error"] = ("unable to parse git-describe output: '%s'" 1056 % describe_out) 1057 return pieces 1058 1059 # tag 1060 full_tag = mo.group(1) 1061 if not full_tag.startswith(tag_prefix): 1062 if verbose: 1063 fmt = "tag '%s' doesn't start with prefix '%s'" 1064 print(fmt % (full_tag, tag_prefix)) 1065 pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" 1066 % (full_tag, tag_prefix)) 1067 return pieces 1068 pieces["closest-tag"] = full_tag[len(tag_prefix):] 1069 1070 # distance: number of commits since tag 1071 pieces["distance"] = int(mo.group(2)) 1072 1073 # commit: short hex revision ID 1074 pieces["short"] = mo.group(3) 1075 1076 else: 1077 # HEX: no tags 1078 pieces["closest-tag"] = None 1079 count_out = run_command(GITS, ["rev-list", "HEAD", "--count"], 1080 cwd=root) 1081 pieces["distance"] = int(count_out) # total number of commits 1082 1083 return pieces 1084 1085 1086def do_vcs_install(manifest_in, versionfile_source, ipy): 1087 GITS = ["git"] 1088 if sys.platform == "win32": 1089 GITS = ["git.cmd", "git.exe"] 1090 files = [manifest_in, versionfile_source] 1091 if ipy: 1092 files.append(ipy) 1093 try: 1094 me = __file__ 1095 if me.endswith(".pyc") or me.endswith(".pyo"): 1096 me = os.path.splitext(me)[0] + ".py" 1097 versioneer_file = os.path.relpath(me) 1098 except NameError: 1099 versioneer_file = "versioneer.py" 1100 files.append(versioneer_file) 1101 present = False 1102 try: 1103 f = open(".gitattributes", "r") 1104 for line in f.readlines(): 1105 if line.strip().startswith(versionfile_source): 1106 if "export-subst" in line.strip().split()[1:]: 1107 present = True 1108 f.close() 1109 except EnvironmentError: 1110 pass 1111 if not present: 1112 f = open(".gitattributes", "a+") 1113 f.write("%s export-subst\n" % versionfile_source) 1114 f.close() 1115 files.append(".gitattributes") 1116 run_command(GITS, ["add", "--"] + files) 1117 1118 1119def versions_from_parentdir(parentdir_prefix, root, verbose): 1120 # Source tarballs conventionally unpack into a directory that includes 1121 # both the project name and a version string. 1122 dirname = os.path.basename(root) 1123 if not dirname.startswith(parentdir_prefix): 1124 if verbose: 1125 print("guessing rootdir is '%s', but '%s' doesn't start with " 1126 "prefix '%s'" % (root, dirname, parentdir_prefix)) 1127 raise NotThisMethod("rootdir doesn't start with parentdir_prefix") 1128 return {"version": dirname[len(parentdir_prefix):], 1129 "full-revisionid": None, 1130 "dirty": False, "error": None} 1131 1132SHORT_VERSION_PY = """ 1133# This file was generated by 'versioneer.py' (0.15) from 1134# revision-control system data, or from the parent directory name of an 1135# unpacked source archive. Distribution tarballs contain a pre-generated copy 1136# of this file. 1137 1138import json 1139import sys 1140 1141version_json = ''' 1142%s 1143''' # END VERSION_JSON 1144 1145 1146def get_versions(): 1147 return json.loads(version_json) 1148""" 1149 1150 1151def versions_from_file(filename): 1152 try: 1153 with open(filename) as f: 1154 contents = f.read() 1155 except EnvironmentError: 1156 raise NotThisMethod("unable to read _version.py") 1157 mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", 1158 contents, re.M | re.S) 1159 if not mo: 1160 raise NotThisMethod("no version_json in _version.py") 1161 return json.loads(mo.group(1)) 1162 1163 1164def write_to_version_file(filename, versions): 1165 os.unlink(filename) 1166 contents = json.dumps(versions, sort_keys=True, 1167 indent=1, separators=(",", ": ")) 1168 with open(filename, "w") as f: 1169 f.write(SHORT_VERSION_PY % contents) 1170 1171 print("set %s to '%s'" % (filename, versions["version"])) 1172 1173 1174def plus_or_dot(pieces): 1175 if "+" in pieces.get("closest-tag", ""): 1176 return "." 1177 return "+" 1178 1179 1180def render_pep440(pieces): 1181 # now build up version string, with post-release "local version 1182 # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you 1183 # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty 1184 1185 # exceptions: 1186 # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] 1187 1188 if pieces["closest-tag"]: 1189 rendered = pieces["closest-tag"] 1190 if pieces["distance"] or pieces["dirty"]: 1191 rendered += plus_or_dot(pieces) 1192 rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) 1193 if pieces["dirty"]: 1194 rendered += ".dirty" 1195 else: 1196 # exception #1 1197 rendered = "0+untagged.%d.g%s" % (pieces["distance"], 1198 pieces["short"]) 1199 if pieces["dirty"]: 1200 rendered += ".dirty" 1201 return rendered 1202 1203 1204def render_pep440_pre(pieces): 1205 # TAG[.post.devDISTANCE] . No -dirty 1206 1207 # exceptions: 1208 # 1: no tags. 0.post.devDISTANCE 1209 1210 if pieces["closest-tag"]: 1211 rendered = pieces["closest-tag"] 1212 if pieces["distance"]: 1213 rendered += ".post.dev%d" % pieces["distance"] 1214 else: 1215 # exception #1 1216 rendered = "0.post.dev%d" % pieces["distance"] 1217 return rendered 1218 1219 1220def render_pep440_post(pieces): 1221 # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that 1222 # .dev0 sorts backwards (a dirty tree will appear "older" than the 1223 # corresponding clean one), but you shouldn't be releasing software with 1224 # -dirty anyways. 1225 1226 # exceptions: 1227 # 1: no tags. 0.postDISTANCE[.dev0] 1228 1229 if pieces["closest-tag"]: 1230 rendered = pieces["closest-tag"] 1231 if pieces["distance"] or pieces["dirty"]: 1232 rendered += ".post%d" % pieces["distance"] 1233 if pieces["dirty"]: 1234 rendered += ".dev0" 1235 rendered += plus_or_dot(pieces) 1236 rendered += "g%s" % pieces["short"] 1237 else: 1238 # exception #1 1239 rendered = "0.post%d" % pieces["distance"] 1240 if pieces["dirty"]: 1241 rendered += ".dev0" 1242 rendered += "+g%s" % pieces["short"] 1243 return rendered 1244 1245 1246def render_pep440_old(pieces): 1247 # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. 1248 1249 # exceptions: 1250 # 1: no tags. 0.postDISTANCE[.dev0] 1251 1252 if pieces["closest-tag"]: 1253 rendered = pieces["closest-tag"] 1254 if pieces["distance"] or pieces["dirty"]: 1255 rendered += ".post%d" % pieces["distance"] 1256 if pieces["dirty"]: 1257 rendered += ".dev0" 1258 else: 1259 # exception #1 1260 rendered = "0.post%d" % pieces["distance"] 1261 if pieces["dirty"]: 1262 rendered += ".dev0" 1263 return rendered 1264 1265 1266def render_git_describe(pieces): 1267 # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty 1268 # --always' 1269 1270 # exceptions: 1271 # 1: no tags. HEX[-dirty] (note: no 'g' prefix) 1272 1273 if pieces["closest-tag"]: 1274 rendered = pieces["closest-tag"] 1275 if pieces["distance"]: 1276 rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 1277 else: 1278 # exception #1 1279 rendered = pieces["short"] 1280 if pieces["dirty"]: 1281 rendered += "-dirty" 1282 return rendered 1283 1284 1285def render_git_describe_long(pieces): 1286 # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty 1287 # --always -long'. The distance/hash is unconditional. 1288 1289 # exceptions: 1290 # 1: no tags. HEX[-dirty] (note: no 'g' prefix) 1291 1292 if pieces["closest-tag"]: 1293 rendered = pieces["closest-tag"] 1294 rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 1295 else: 1296 # exception #1 1297 rendered = pieces["short"] 1298 if pieces["dirty"]: 1299 rendered += "-dirty" 1300 return rendered 1301 1302 1303def render(pieces, style): 1304 if pieces["error"]: 1305 return {"version": "unknown", 1306 "full-revisionid": pieces.get("long"), 1307 "dirty": None, 1308 "error": pieces["error"]} 1309 1310 if not style or style == "default": 1311 style = "pep440" # the default 1312 1313 if style == "pep440": 1314 rendered = render_pep440(pieces) 1315 elif style == "pep440-pre": 1316 rendered = render_pep440_pre(pieces) 1317 elif style == "pep440-post": 1318 rendered = render_pep440_post(pieces) 1319 elif style == "pep440-old": 1320 rendered = render_pep440_old(pieces) 1321 elif style == "git-describe": 1322 rendered = render_git_describe(pieces) 1323 elif style == "git-describe-long": 1324 rendered = render_git_describe_long(pieces) 1325 else: 1326 raise ValueError("unknown style '%s'" % style) 1327 1328 return {"version": rendered, "full-revisionid": pieces["long"], 1329 "dirty": pieces["dirty"], "error": None} 1330 1331 1332class VersioneerBadRootError(Exception): 1333 pass 1334 1335 1336def get_versions(verbose=False): 1337 # returns dict with two keys: 'version' and 'full' 1338 1339 if "versioneer" in sys.modules: 1340 # see the discussion in cmdclass.py:get_cmdclass() 1341 del sys.modules["versioneer"] 1342 1343 root = get_root() 1344 cfg = get_config_from_root(root) 1345 1346 assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" 1347 handlers = HANDLERS.get(cfg.VCS) 1348 assert handlers, "unrecognized VCS '%s'" % cfg.VCS 1349 verbose = verbose or cfg.verbose 1350 assert cfg.versionfile_source is not None, \ 1351 "please set versioneer.versionfile_source" 1352 assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" 1353 1354 versionfile_abs = os.path.join(root, cfg.versionfile_source) 1355 1356 # extract version from first of: _version.py, VCS command (e.g. 'git 1357 # describe'), parentdir. This is meant to work for developers using a 1358 # source checkout, for users of a tarball created by 'setup.py sdist', 1359 # and for users of a tarball/zipball created by 'git archive' or github's 1360 # download-from-tag feature or the equivalent in other VCSes. 1361 1362 get_keywords_f = handlers.get("get_keywords") 1363 from_keywords_f = handlers.get("keywords") 1364 if get_keywords_f and from_keywords_f: 1365 try: 1366 keywords = get_keywords_f(versionfile_abs) 1367 ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) 1368 if verbose: 1369 print("got version from expanded keyword %s" % ver) 1370 return ver 1371 except NotThisMethod: 1372 pass 1373 1374 try: 1375 ver = versions_from_file(versionfile_abs) 1376 if verbose: 1377 print("got version from file %s %s" % (versionfile_abs, ver)) 1378 return ver 1379 except NotThisMethod: 1380 pass 1381 1382 from_vcs_f = handlers.get("pieces_from_vcs") 1383 if from_vcs_f: 1384 try: 1385 pieces = from_vcs_f(cfg.tag_prefix, root, verbose) 1386 ver = render(pieces, cfg.style) 1387 if verbose: 1388 print("got version from VCS %s" % ver) 1389 return ver 1390 except NotThisMethod: 1391 pass 1392 1393 try: 1394 if cfg.parentdir_prefix: 1395 ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) 1396 if verbose: 1397 print("got version from parentdir %s" % ver) 1398 return ver 1399 except NotThisMethod: 1400 pass 1401 1402 if verbose: 1403 print("unable to compute version") 1404 1405 return {"version": "0+unknown", "full-revisionid": None, 1406 "dirty": None, "error": "unable to compute version"} 1407 1408 1409def get_version(): 1410 return get_versions()["version"] 1411 1412 1413def get_cmdclass(): 1414 if "versioneer" in sys.modules: 1415 del sys.modules["versioneer"] 1416 # this fixes the "python setup.py develop" case (also 'install' and 1417 # 'easy_install .'), in which subdependencies of the main project are 1418 # built (using setup.py bdist_egg) in the same python process. Assume 1419 # a main project A and a dependency B, which use different versions 1420 # of Versioneer. A's setup.py imports A's Versioneer, leaving it in 1421 # sys.modules by the time B's setup.py is executed, causing B to run 1422 # with the wrong versioneer. Setuptools wraps the sub-dep builds in a 1423 # sandbox that restores sys.modules to it's pre-build state, so the 1424 # parent is protected against the child's "import versioneer". By 1425 # removing ourselves from sys.modules here, before the child build 1426 # happens, we protect the child from the parent's versioneer too. 1427 # Also see https://github.com/warner/python-versioneer/issues/52 1428 1429 cmds = {} 1430 1431 # we add "version" to both distutils and setuptools 1432 from distutils.core import Command 1433 1434 class cmd_version(Command): 1435 description = "report generated version string" 1436 user_options = [] 1437 boolean_options = [] 1438 1439 def initialize_options(self): 1440 pass 1441 1442 def finalize_options(self): 1443 pass 1444 1445 def run(self): 1446 vers = get_versions(verbose=True) 1447 print("Version: %s" % vers["version"]) 1448 print(" full-revisionid: %s" % vers.get("full-revisionid")) 1449 print(" dirty: %s" % vers.get("dirty")) 1450 if vers["error"]: 1451 print(" error: %s" % vers["error"]) 1452 cmds["version"] = cmd_version 1453 1454 # we override "build_py" in both distutils and setuptools 1455 # 1456 # most invocation pathways end up running build_py: 1457 # distutils/build -> build_py 1458 # distutils/install -> distutils/build ->.. 1459 # setuptools/bdist_wheel -> distutils/install ->.. 1460 # setuptools/bdist_egg -> distutils/install_lib -> build_py 1461 # setuptools/install -> bdist_egg ->.. 1462 # setuptools/develop -> ? 1463 1464 from distutils.command.build_py import build_py as _build_py 1465 1466 class cmd_build_py(_build_py): 1467 def run(self): 1468 root = get_root() 1469 cfg = get_config_from_root(root) 1470 versions = get_versions() 1471 _build_py.run(self) 1472 # now locate _version.py in the new build/ directory and replace 1473 # it with an updated value 1474 if cfg.versionfile_build: 1475 target_versionfile = os.path.join(self.build_lib, 1476 cfg.versionfile_build) 1477 print("UPDATING %s" % target_versionfile) 1478 write_to_version_file(target_versionfile, versions) 1479 cmds["build_py"] = cmd_build_py 1480 1481 if "cx_Freeze" in sys.modules: # cx_freeze enabled? 1482 from cx_Freeze.dist import build_exe as _build_exe 1483 1484 class cmd_build_exe(_build_exe): 1485 def run(self): 1486 root = get_root() 1487 cfg = get_config_from_root(root) 1488 versions = get_versions() 1489 target_versionfile = cfg.versionfile_source 1490 print("UPDATING %s" % target_versionfile) 1491 write_to_version_file(target_versionfile, versions) 1492 1493 _build_exe.run(self) 1494 os.unlink(target_versionfile) 1495 with open(cfg.versionfile_source, "w") as f: 1496 LONG = LONG_VERSION_PY[cfg.VCS] 1497 f.write(LONG % 1498 {"DOLLAR": "$", 1499 "STYLE": cfg.style, 1500 "TAG_PREFIX": cfg.tag_prefix, 1501 "PARENTDIR_PREFIX": cfg.parentdir_prefix, 1502 "VERSIONFILE_SOURCE": cfg.versionfile_source, 1503 }) 1504 cmds["build_exe"] = cmd_build_exe 1505 del cmds["build_py"] 1506 1507 # we override different "sdist" commands for both environments 1508 if "setuptools" in sys.modules: 1509 from setuptools.command.sdist import sdist as _sdist 1510 else: 1511 from distutils.command.sdist import sdist as _sdist 1512 1513 class cmd_sdist(_sdist): 1514 def run(self): 1515 versions = get_versions() 1516 self._versioneer_generated_versions = versions 1517 # unless we update this, the command will keep using the old 1518 # version 1519 self.distribution.metadata.version = versions["version"] 1520 return _sdist.run(self) 1521 1522 def make_release_tree(self, base_dir, files): 1523 root = get_root() 1524 cfg = get_config_from_root(root) 1525 _sdist.make_release_tree(self, base_dir, files) 1526 # now locate _version.py in the new base_dir directory 1527 # (remembering that it may be a hardlink) and replace it with an 1528 # updated value 1529 target_versionfile = os.path.join(base_dir, cfg.versionfile_source) 1530 print("UPDATING %s" % target_versionfile) 1531 write_to_version_file(target_versionfile, 1532 self._versioneer_generated_versions) 1533 cmds["sdist"] = cmd_sdist 1534 1535 return cmds 1536 1537 1538CONFIG_ERROR = """ 1539setup.cfg is missing the necessary Versioneer configuration. You need 1540a section like: 1541 1542 [versioneer] 1543 VCS = git 1544 style = pep440 1545 versionfile_source = src/myproject/_version.py 1546 versionfile_build = myproject/_version.py 1547 tag_prefix = "" 1548 parentdir_prefix = myproject- 1549 1550You will also need to edit your setup.py to use the results: 1551 1552 import versioneer 1553 setup(version=versioneer.get_version(), 1554 cmdclass=versioneer.get_cmdclass(), ...) 1555 1556Please read the docstring in ./versioneer.py for configuration instructions, 1557edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. 1558""" 1559 1560SAMPLE_CONFIG = """ 1561# See the docstring in versioneer.py for instructions. Note that you must 1562# re-run 'versioneer.py setup' after changing this section, and commit the 1563# resulting files. 1564 1565[versioneer] 1566#VCS = git 1567#style = pep440 1568#versionfile_source = 1569#versionfile_build = 1570#tag_prefix = 1571#parentdir_prefix = 1572 1573""" 1574 1575INIT_PY_SNIPPET = """ 1576from ._version import get_versions 1577__version__ = get_versions()['version'] 1578del get_versions 1579""" 1580 1581 1582def do_setup(): 1583 root = get_root() 1584 try: 1585 cfg = get_config_from_root(root) 1586 except (EnvironmentError, configparser.NoSectionError, 1587 configparser.NoOptionError) as e: 1588 if isinstance(e, (EnvironmentError, configparser.NoSectionError)): 1589 print("Adding sample versioneer config to setup.cfg", 1590 file=sys.stderr) 1591 with open(os.path.join(root, "setup.cfg"), "a") as f: 1592 f.write(SAMPLE_CONFIG) 1593 print(CONFIG_ERROR, file=sys.stderr) 1594 return 1 1595 1596 print(" creating %s" % cfg.versionfile_source) 1597 with open(cfg.versionfile_source, "w") as f: 1598 LONG = LONG_VERSION_PY[cfg.VCS] 1599 f.write(LONG % {"DOLLAR": "$", 1600 "STYLE": cfg.style, 1601 "TAG_PREFIX": cfg.tag_prefix, 1602 "PARENTDIR_PREFIX": cfg.parentdir_prefix, 1603 "VERSIONFILE_SOURCE": cfg.versionfile_source, 1604 }) 1605 1606 ipy = os.path.join(os.path.dirname(cfg.versionfile_source), 1607 "__init__.py") 1608 if os.path.exists(ipy): 1609 try: 1610 with open(ipy, "r") as f: 1611 old = f.read() 1612 except EnvironmentError: 1613 old = "" 1614 if INIT_PY_SNIPPET not in old: 1615 print(" appending to %s" % ipy) 1616 with open(ipy, "a") as f: 1617 f.write(INIT_PY_SNIPPET) 1618 else: 1619 print(" %s unmodified" % ipy) 1620 else: 1621 print(" %s doesn't exist, ok" % ipy) 1622 ipy = None 1623 1624 # Make sure both the top-level "versioneer.py" and versionfile_source 1625 # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so 1626 # they'll be copied into source distributions. Pip won't be able to 1627 # install the package without this. 1628 manifest_in = os.path.join(root, "MANIFEST.in") 1629 simple_includes = set() 1630 try: 1631 with open(manifest_in, "r") as f: 1632 for line in f: 1633 if line.startswith("include "): 1634 for include in line.split()[1:]: 1635 simple_includes.add(include) 1636 except EnvironmentError: 1637 pass 1638 # That doesn't cover everything MANIFEST.in can do 1639 # (http://docs.python.org/2/distutils/sourcedist.html#commands), so 1640 # it might give some false negatives. Appending redundant 'include' 1641 # lines is safe, though. 1642 if "versioneer.py" not in simple_includes: 1643 print(" appending 'versioneer.py' to MANIFEST.in") 1644 with open(manifest_in, "a") as f: 1645 f.write("include versioneer.py\n") 1646 else: 1647 print(" 'versioneer.py' already in MANIFEST.in") 1648 if cfg.versionfile_source not in simple_includes: 1649 print(" appending versionfile_source ('%s') to MANIFEST.in" % 1650 cfg.versionfile_source) 1651 with open(manifest_in, "a") as f: 1652 f.write("include %s\n" % cfg.versionfile_source) 1653 else: 1654 print(" versionfile_source already in MANIFEST.in") 1655 1656 # Make VCS-specific changes. For git, this means creating/changing 1657 # .gitattributes to mark _version.py for export-time keyword 1658 # substitution. 1659 do_vcs_install(manifest_in, cfg.versionfile_source, ipy) 1660 return 0 1661 1662 1663def scan_setup_py(): 1664 found = set() 1665 setters = False 1666 errors = 0 1667 with open("setup.py", "r") as f: 1668 for line in f.readlines(): 1669 if "import versioneer" in line: 1670 found.add("import") 1671 if "versioneer.get_cmdclass()" in line: 1672 found.add("cmdclass") 1673 if "versioneer.get_version()" in line: 1674 found.add("get_version") 1675 if "versioneer.VCS" in line: 1676 setters = True 1677 if "versioneer.versionfile_source" in line: 1678 setters = True 1679 if len(found) != 3: 1680 print("") 1681 print("Your setup.py appears to be missing some important items") 1682 print("(but I might be wrong). Please make sure it has something") 1683 print("roughly like the following:") 1684 print("") 1685 print(" import versioneer") 1686 print(" setup( version=versioneer.get_version(),") 1687 print(" cmdclass=versioneer.get_cmdclass(), ...)") 1688 print("") 1689 errors += 1 1690 if setters: 1691 print("You should remove lines like 'versioneer.VCS = ' and") 1692 print("'versioneer.versionfile_source = ' . This configuration") 1693 print("now lives in setup.cfg, and should be removed from setup.py") 1694 print("") 1695 errors += 1 1696 return errors 1697 1698if __name__ == "__main__": 1699 cmd = sys.argv[1] 1700 if cmd == "setup": 1701 errors = do_setup() 1702 errors += scan_setup_py() 1703 if errors: 1704 sys.exit(1) 1705