Completed
Push — master ( 8cb22a...473d55 )
by John
01:52
created

versioneer.py (7 issues)

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