Issues (71)

versioneer.py (9 issues)

1
2
# Version: 0.15
3
4
"""
5
The Versioneer
6
==============
7
8
* like a rocketeer, but for versions!
9
* https://github.com/warner/python-versioneer
10
* Brian Warner
11
* License: Public Domain
12
* Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, and pypy
13
* [![Latest Version]
14
(https://pypip.in/version/versioneer/badge.svg?style=flat)
15
](https://pypi.python.org/pypi/versioneer/)
16
* [![Build Status]
17
(https://travis-ci.org/warner/python-versioneer.png?branch=master)
18
](https://travis-ci.org/warner/python-versioneer)
19
20
This is a tool for managing a recorded version number in distutils-based
21
python projects. The goal is to remove the tedious and error-prone "update
22
the embedded version string" step from your release process. Making a new
23
release should be as easy as recording a new tag in your version-control
24
system, and maybe making new tarballs.
25
26
27
## Quick Install
28
29
* `pip install versioneer` to somewhere to your $PATH
30
* add a `[versioneer]` section to your setup.cfg (see below)
31
* run `versioneer install` in your source tree, commit the results
32
33
## Version Identifiers
34
35
Source trees come from a variety of places:
36
37
* a version-control system checkout (mostly used by developers)
38
* a nightly tarball, produced by build automation
39
* a snapshot tarball, produced by a web-based VCS browser, like github's
40
  "tarball from tag" feature
41
* a release tarball, produced by "setup.py sdist", distributed through PyPI
42
43
Within each source tree, the version identifier (either a string or a number,
44
this tool is format-agnostic) can come from a variety of places:
45
46
* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows
47
  about recent "tags" and an absolute revision-id
48
* the name of the directory into which the tarball was unpacked
49
* an expanded VCS keyword ($Id$, etc)
50
* a `_version.py` created by some earlier build step
51
52
For released software, the version identifier is closely related to a VCS
53
tag. Some projects use tag names that include more than just the version
54
string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool
55
needs to strip the tag prefix to extract the version identifier. For
56
unreleased software (between tags), the version identifier should provide
57
enough information to help developers recreate the same tree, while also
58
giving them an idea of roughly how old the tree is (after version 1.2, before
59
version 1.3). Many VCS systems can report a description that captures this,
60
for example `git describe --tags --dirty --always` reports things like
61
"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the
62
0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has
63
uncommitted changes.
64
65
The version identifier is used for multiple purposes:
66
67
* to allow the module to self-identify its version: `myproject.__version__`
68
* to choose a name and prefix for a 'setup.py sdist' tarball
69
70
## Theory of Operation
71
72
Versioneer works by adding a special `_version.py` file into your source
73
tree, where your `__init__.py` can import it. This `_version.py` knows how to
74
dynamically ask the VCS tool for version information at import time.
75
76
`_version.py` also contains `$Revision$` markers, and the installation
77
process marks `_version.py` to have this marker rewritten with a tag name
78
during the `git archive` command. As a result, generated tarballs will
79
contain enough information to get the proper version.
80
81
To allow `setup.py` to compute a version too, a `versioneer.py` is added to
82
the top level of your source tree, next to `setup.py` and the `setup.cfg`
83
that configures it. This overrides several distutils/setuptools commands to
84
compute the version when invoked, and changes `setup.py build` and `setup.py
85
sdist` to replace `_version.py` with a small static file that contains just
86
the generated version data.
87
88
## Installation
89
90
First, 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
146
This tool provides one script, named `versioneer`. That script has one mode,
147
"install", which writes a copy of `versioneer.py` into the current directory
148
and runs `versioneer.py setup` to finish the installation.
149
150
To 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
191
Once established, all uses of your tree from a VCS checkout should get the
192
current version string. All generated tarballs should include an embedded
193
version string (so users who unpack them will not need a VCS tool installed).
194
195
If you distribute your project through PyPI, then the release process should
196
boil down to two steps:
197
198
* 1: git tag 1.0
199
* 2: python setup.py register sdist upload
200
201
If you distribute it through github (i.e. users use github to generate
202
tarballs with `git archive`), the process is:
203
204
* 1: git tag 1.0
205
* 2: git push; git push --tags
206
207
Versioneer will report "0+untagged.NUMCOMMITS.gHASH" until your tree has at
208
least one tag in its history.
209
210
## Version-String Flavors
211
212
Code which uses Versioneer can learn about its version string at runtime by
213
importing `_version` from your main `__init__.py` file and running the
214
`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can
215
import the top-level `versioneer.py` and run `get_versions()`.
216
217
Both functions return a dictionary with different flavors of version
218
information:
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
238
Some variants are more useful than others. Including `full-revisionid` in a
239
bug 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
241
developers). `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
243
of bugs fixed in various releases.
244
245
The installer adds the following text to your `__init__.py` to place a basic
246
version in `YOURPROJECT.__version__`:
247
248
    from ._version import get_versions
249
    __version__ = get_versions()['version']
250
    del get_versions
251
252
## Styles
253
254
The setup.cfg `style=` configuration controls how the VCS information is
255
rendered into a version string.
256
257
The default style, "pep440", produces a PEP440-compliant string, equal to the
258
un-prefixed tag name for actual releases, and containing an additional "local
259
version" section with more detail for in-between builds. For Git, this is
260
TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags
261
--dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the
262
tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and
263
that this commit is two revisions ("+2") beyond the "0.11" tag. For released
264
software (exactly equal to a known tag), the identifier will only contain the
265
stripped tag, e.g. "0.11".
266
267
Other styles are available. See details.md in the Versioneer source tree for
268
descriptions.
269
270
## Debugging
271
272
Versioneer tries to avoid fatal errors: if something goes wrong, it will tend
273
to return a version of "0+unknown". To investigate the problem, run `setup.py
274
version`, which will run the version-lookup code in a verbose mode, and will
275
display the full contents of `get_versions()` (including the `error` string,
276
which may help identify what went wrong).
277
278
## Updating Versioneer
279
280
To 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
291
Starting with this version, Versioneer is configured with a `[versioneer]`
292
section in your `setup.cfg` file. Earlier versions required the `setup.py` to
293
set attributes on the `versioneer` module immediately after import. The new
294
version will refuse to run (raising an exception during import) until you
295
have provided the necessary `setup.cfg` section.
296
297
In addition, the Versioneer package provides an executable named
298
`versioneer`, and the installation process is driven by running `versioneer
299
install`. 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
304
0.14 changes the format of the version string. 0.13 and earlier used
305
hyphen-separated strings like "0.11-2-g1076c97-dirty". 0.14 and beyond use a
306
plus-separated "local version" section strings, with dot-separated
307
components, like "0.11+2.g1076c97". PEP440-strict tools did not like the old
308
format, but should be ok with the new one.
309
310
### Upgrading from 0.11 to 0.12
311
312
Nothing special.
313
314
### Upgrading from 0.10 to 0.11
315
316
You 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
318
version-control systems (SVN, etc) in the future.
319
320
## Future Directions
321
322
This tool is designed to make it easily extended to other version-control
323
systems: all VCS-specific components are in separate directories like
324
src/git/ . The top-level `versioneer.py` script is assembled from these
325
components by running make-versioneer.py . In the future, make-versioneer.py
326
will 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
328
configuration arguments that are currently provided manually during
329
installation by editing setup.py . Alternatively, it might go the other
330
direction and include code from all supported VCS systems, reducing the
331
number of intermediate scripts.
332
333
334
## License
335
336
To make Versioneer easier to embed, all its code is hereby released into the
337
public domain. The `_version.py` that it creates is also in the public
338
domain.
339
340
"""
341
342
from __future__ import print_function
343
try:
344
    import configparser
345
except ImportError:
346
    import ConfigParser as configparser
347
import errno
348
import json
349
import os
350
import re
351
import subprocess
352
import sys
353
354
355
class VersioneerConfig:
356
    pass
357
358
359
def 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
393
def get_config_from_root(root):
394
    # This might raise EnvironmentError (if setup.cfg is missing), or
395
    # configparser.NoSectionError (if it lacks a [versioneer] section), or
396
    # configparser.NoOptionError (if it lacks "VCS="). See the docstring at
397
    # the top of versioneer.py for instructions on writing your setup.cfg .
398
    setup_cfg = os.path.join(root, "setup.cfg")
399
    parser = configparser.SafeConfigParser()
400
    with open(setup_cfg, "r") as f:
401
        parser.readfp(f)
402
    VCS = parser.get("versioneer", "VCS")  # mandatory
403
404
    def get(parser, name):
405
        if parser.has_option("versioneer", name):
406
            return parser.get("versioneer", name)
407
        return None
408
    cfg = VersioneerConfig()
409
    cfg.VCS = VCS
410
    cfg.style = get(parser, "style") or ""
411
    cfg.versionfile_source = get(parser, "versionfile_source")
412
    cfg.versionfile_build = get(parser, "versionfile_build")
413
    cfg.tag_prefix = get(parser, "tag_prefix")
414
    cfg.parentdir_prefix = get(parser, "parentdir_prefix")
415
    cfg.verbose = get(parser, "verbose")
416
    return cfg
417
418
419
class NotThisMethod(Exception):
420
    pass
421
422
# these dictionaries contain VCS-specific tools
423
LONG_VERSION_PY = {}
424
HANDLERS = {}
425
426
427
def register_vcs_handler(vcs, method):  # decorator
428
    def decorate(f):
429
        if vcs not in HANDLERS:
430
            HANDLERS[vcs] = {}
431
        HANDLERS[vcs][method] = f
432
        return f
433
    return decorate
434
435
436
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.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
437
    assert isinstance(commands, list)
438
    p = None
439
    for c in commands:
440
        try:
441
            dispcmd = str([c] + args)
442
            # remember shell=False, so use git.cmd on windows, not just git
443
            p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
444
                                 stderr=(subprocess.PIPE if hide_stderr
445
                                         else None))
446
            break
447
        except EnvironmentError:
448
            e = sys.exc_info()[1]
449
            if e.errno == errno.ENOENT:
450
                continue
451
            if verbose:
452
                print("unable to run %s" % dispcmd)
453
                print(e)
454
            return None
455
    else:
456
        if verbose:
457
            print("unable to find command, tried %s" % (commands,))
458
        return None
459
    stdout = p.communicate()[0].strip()
460
    if sys.version_info[0] >= 3:
461
        stdout = stdout.decode()
462
    if p.returncode != 0:
463
        if verbose:
464
            print("unable to run %s (error)" % dispcmd)
465
        return None
466
    return stdout
467
LONG_VERSION_PY['git'] = '''
468
# This file helps to compute a version number in source trees obtained from
469
# git-archive tarball (such as those provided by githubs download-from-tag
470
# feature). Distribution tarballs (built by setup.py sdist) and build
471
# directories (produced by setup.py build) will contain a much shorter file
472
# that just contains the computed version number.
473
474
# This file is released into the public domain. Generated by
475
# versioneer-0.15 (https://github.com/warner/python-versioneer)
476
477
import errno
478
import os
479
import re
480
import subprocess
481
import sys
482
483
484
def get_keywords():
485
    # these strings will be replaced by git during git-archive.
486
    # setup.py/versioneer.py will grep for the variable names, so they must
487
    # each be defined on a line of their own. _version.py will just call
488
    # get_keywords().
489
    git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s"
490
    git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s"
491
    keywords = {"refnames": git_refnames, "full": git_full}
492
    return keywords
493
494
495
class VersioneerConfig:
496
    pass
497
498
499
def get_config():
500
    # these strings are filled in when 'setup.py versioneer' creates
501
    # _version.py
502
    cfg = VersioneerConfig()
503
    cfg.VCS = "git"
504
    cfg.style = "%(STYLE)s"
505
    cfg.tag_prefix = "%(TAG_PREFIX)s"
506
    cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s"
507
    cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s"
508
    cfg.verbose = False
509
    return cfg
510
511
512
class NotThisMethod(Exception):
513
    pass
514
515
516
LONG_VERSION_PY = {}
517
HANDLERS = {}
518
519
520
def register_vcs_handler(vcs, method):  # decorator
521
    def decorate(f):
522
        if vcs not in HANDLERS:
523
            HANDLERS[vcs] = {}
524
        HANDLERS[vcs][method] = f
525
        return f
526
    return decorate
527
528
529
def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
530
    assert isinstance(commands, list)
531
    p = None
532
    for c in commands:
533
        try:
534
            dispcmd = str([c] + args)
535
            # remember shell=False, so use git.cmd on windows, not just git
536
            p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
537
                                 stderr=(subprocess.PIPE if hide_stderr
538
                                         else None))
539
            break
540
        except EnvironmentError:
541
            e = sys.exc_info()[1]
542
            if e.errno == errno.ENOENT:
543
                continue
544
            if verbose:
545
                print("unable to run %%s" %% dispcmd)
546
                print(e)
547
            return None
548
    else:
549
        if verbose:
550
            print("unable to find command, tried %%s" %% (commands,))
551
        return None
552
    stdout = p.communicate()[0].strip()
553
    if sys.version_info[0] >= 3:
554
        stdout = stdout.decode()
555
    if p.returncode != 0:
556
        if verbose:
557
            print("unable to run %%s (error)" %% dispcmd)
558
        return None
559
    return stdout
560
561
562
def versions_from_parentdir(parentdir_prefix, root, verbose):
563
    # Source tarballs conventionally unpack into a directory that includes
564
    # both the project name and a version string.
565
    dirname = os.path.basename(root)
566
    if not dirname.startswith(parentdir_prefix):
567
        if verbose:
568
            print("guessing rootdir is '%%s', but '%%s' doesn't start with "
569
                  "prefix '%%s'" %% (root, dirname, parentdir_prefix))
570
        raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
571
    return {"version": dirname[len(parentdir_prefix):],
572
            "full-revisionid": None,
573
            "dirty": False, "error": None}
574
575
576
@register_vcs_handler("git", "get_keywords")
577
def git_get_keywords(versionfile_abs):
578
    # the code embedded in _version.py can just fetch the value of these
579
    # keywords. When used from setup.py, we don't want to import _version.py,
580
    # so we do it with a regexp instead. This function is not used from
581
    # _version.py.
582
    keywords = {}
583
    try:
584
        f = open(versionfile_abs, "r")
585
        for line in f.readlines():
586
            if line.strip().startswith("git_refnames ="):
587
                mo = re.search(r'=\s*"(.*)"', line)
588
                if mo:
589
                    keywords["refnames"] = mo.group(1)
590
            if line.strip().startswith("git_full ="):
591
                mo = re.search(r'=\s*"(.*)"', line)
592
                if mo:
593
                    keywords["full"] = mo.group(1)
594
        f.close()
595
    except EnvironmentError:
596
        pass
597
    return keywords
598
599
600
@register_vcs_handler("git", "keywords")
601
def git_versions_from_keywords(keywords, tag_prefix, verbose):
602
    if not keywords:
603
        raise NotThisMethod("no keywords at all, weird")
604
    refnames = keywords["refnames"].strip()
605
    if refnames.startswith("$Format"):
606
        if verbose:
607
            print("keywords are unexpanded, not using")
608
        raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
609
    refs = set([r.strip() for r in refnames.strip("()").split(",")])
610
    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
611
    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
612
    TAG = "tag: "
613
    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
614
    if not tags:
615
        # Either we're using git < 1.8.3, or there really are no tags. We use
616
        # a heuristic: assume all version tags have a digit. The old git %%d
617
        # expansion behaves like git log --decorate=short and strips out the
618
        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
619
        # between branches and tags. By ignoring refnames without digits, we
620
        # filter out many common branch names like "release" and
621
        # "stabilization", as well as "HEAD" and "master".
622
        tags = set([r for r in refs if re.search(r'\d', r)])
623
        if verbose:
624
            print("discarding '%%s', no digits" %% ",".join(refs-tags))
625
    if verbose:
626
        print("likely tags: %%s" %% ",".join(sorted(tags)))
627
    for ref in sorted(tags):
628
        # sorting will prefer e.g. "2.0" over "2.0rc1"
629
        if ref.startswith(tag_prefix):
630
            r = ref[len(tag_prefix):]
631
            if verbose:
632
                print("picking %%s" %% r)
633
            return {"version": r,
634
                    "full-revisionid": keywords["full"].strip(),
635
                    "dirty": False, "error": None
636
                    }
637
    # no suitable tags, so version is "0+unknown", but full hex is still there
638
    if verbose:
639
        print("no suitable tags, using unknown + full revision id")
640
    return {"version": "0+unknown",
641
            "full-revisionid": keywords["full"].strip(),
642
            "dirty": False, "error": "no suitable tags"}
643
644
645
@register_vcs_handler("git", "pieces_from_vcs")
646
def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
647
    # this runs 'git' from the root of the source tree. This only gets called
648
    # if the git-archive 'subst' keywords were *not* expanded, and
649
    # _version.py hasn't already been rewritten with a short version string,
650
    # meaning we're inside a checked out source tree.
651
652
    if not os.path.exists(os.path.join(root, ".git")):
653
        if verbose:
654
            print("no .git in %%s" %% root)
655
        raise NotThisMethod("no .git directory")
656
657
    GITS = ["git"]
658
    if sys.platform == "win32":
659
        GITS = ["git.cmd", "git.exe"]
660
    # if there is a tag, this yields TAG-NUM-gHEX[-dirty]
661
    # if there are no tags, this yields HEX[-dirty] (no NUM)
662
    describe_out = run_command(GITS, ["describe", "--tags", "--dirty",
663
                                      "--always", "--long"],
664
                               cwd=root)
665
    # --long was added in git-1.5.5
666
    if describe_out is None:
667
        raise NotThisMethod("'git describe' failed")
668
    describe_out = describe_out.strip()
669
    full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
670
    if full_out is None:
671
        raise NotThisMethod("'git rev-parse' failed")
672
    full_out = full_out.strip()
673
674
    pieces = {}
675
    pieces["long"] = full_out
676
    pieces["short"] = full_out[:7]  # maybe improved later
677
    pieces["error"] = None
678
679
    # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
680
    # TAG might have hyphens.
681
    git_describe = describe_out
682
683
    # look for -dirty suffix
684
    dirty = git_describe.endswith("-dirty")
685
    pieces["dirty"] = dirty
686
    if dirty:
687
        git_describe = git_describe[:git_describe.rindex("-dirty")]
688
689
    # now we have TAG-NUM-gHEX or HEX
690
691
    if "-" in git_describe:
692
        # TAG-NUM-gHEX
693
        mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
694
        if not mo:
695
            # unparseable. Maybe git-describe is misbehaving?
696
            pieces["error"] = ("unable to parse git-describe output: '%%s'"
697
                               %% describe_out)
698
            return pieces
699
700
        # tag
701
        full_tag = mo.group(1)
702
        if not full_tag.startswith(tag_prefix):
703
            if verbose:
704
                fmt = "tag '%%s' doesn't start with prefix '%%s'"
705
                print(fmt %% (full_tag, tag_prefix))
706
            pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'"
707
                               %% (full_tag, tag_prefix))
708
            return pieces
709
        pieces["closest-tag"] = full_tag[len(tag_prefix):]
710
711
        # distance: number of commits since tag
712
        pieces["distance"] = int(mo.group(2))
713
714
        # commit: short hex revision ID
715
        pieces["short"] = mo.group(3)
716
717
    else:
718
        # HEX: no tags
719
        pieces["closest-tag"] = None
720
        count_out = run_command(GITS, ["rev-list", "HEAD", "--count"],
721
                                cwd=root)
722
        pieces["distance"] = int(count_out)  # total number of commits
723
724
    return pieces
725
726
727
def plus_or_dot(pieces):
728
    if "+" in pieces.get("closest-tag", ""):
729
        return "."
730
    return "+"
731
732
733
def render_pep440(pieces):
734
    # now build up version string, with post-release "local version
735
    # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
736
    # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
737
738
    # exceptions:
739
    # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
740
741
    if pieces["closest-tag"]:
742
        rendered = pieces["closest-tag"]
743
        if pieces["distance"] or pieces["dirty"]:
744
            rendered += plus_or_dot(pieces)
745
            rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"])
746
            if pieces["dirty"]:
747
                rendered += ".dirty"
748
    else:
749
        # exception #1
750
        rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"],
751
                                          pieces["short"])
752
        if pieces["dirty"]:
753
            rendered += ".dirty"
754
    return rendered
755
756
757
def render_pep440_pre(pieces):
758
    # TAG[.post.devDISTANCE] . No -dirty
759
760
    # exceptions:
761
    # 1: no tags. 0.post.devDISTANCE
762
763
    if pieces["closest-tag"]:
764
        rendered = pieces["closest-tag"]
765
        if pieces["distance"]:
766
            rendered += ".post.dev%%d" %% pieces["distance"]
767
    else:
768
        # exception #1
769
        rendered = "0.post.dev%%d" %% pieces["distance"]
770
    return rendered
771
772
773
def render_pep440_post(pieces):
774
    # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that
775
    # .dev0 sorts backwards (a dirty tree will appear "older" than the
776
    # corresponding clean one), but you shouldn't be releasing software with
777
    # -dirty anyways.
778
779
    # exceptions:
780
    # 1: no tags. 0.postDISTANCE[.dev0]
781
782
    if pieces["closest-tag"]:
783
        rendered = pieces["closest-tag"]
784
        if pieces["distance"] or pieces["dirty"]:
785
            rendered += ".post%%d" %% pieces["distance"]
786
            if pieces["dirty"]:
787
                rendered += ".dev0"
788
            rendered += plus_or_dot(pieces)
789
            rendered += "g%%s" %% pieces["short"]
790
    else:
791
        # exception #1
792
        rendered = "0.post%%d" %% pieces["distance"]
793
        if pieces["dirty"]:
794
            rendered += ".dev0"
795
        rendered += "+g%%s" %% pieces["short"]
796
    return rendered
797
798
799
def render_pep440_old(pieces):
800
    # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty.
801
802
    # exceptions:
803
    # 1: no tags. 0.postDISTANCE[.dev0]
804
805
    if pieces["closest-tag"]:
806
        rendered = pieces["closest-tag"]
807
        if pieces["distance"] or pieces["dirty"]:
808
            rendered += ".post%%d" %% pieces["distance"]
809
            if pieces["dirty"]:
810
                rendered += ".dev0"
811
    else:
812
        # exception #1
813
        rendered = "0.post%%d" %% pieces["distance"]
814
        if pieces["dirty"]:
815
            rendered += ".dev0"
816
    return rendered
817
818
819
def render_git_describe(pieces):
820
    # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty
821
    # --always'
822
823
    # exceptions:
824
    # 1: no tags. HEX[-dirty]  (note: no 'g' prefix)
825
826
    if pieces["closest-tag"]:
827
        rendered = pieces["closest-tag"]
828
        if pieces["distance"]:
829
            rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
830
    else:
831
        # exception #1
832
        rendered = pieces["short"]
833
    if pieces["dirty"]:
834
        rendered += "-dirty"
835
    return rendered
836
837
838
def render_git_describe_long(pieces):
839
    # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty
840
    # --always -long'. The distance/hash is unconditional.
841
842
    # exceptions:
843
    # 1: no tags. HEX[-dirty]  (note: no 'g' prefix)
844
845
    if pieces["closest-tag"]:
846
        rendered = pieces["closest-tag"]
847
        rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"])
848
    else:
849
        # exception #1
850
        rendered = pieces["short"]
851
    if pieces["dirty"]:
852
        rendered += "-dirty"
853
    return rendered
854
855
856
def render(pieces, style):
857
    if pieces["error"]:
858
        return {"version": "unknown",
859
                "full-revisionid": pieces.get("long"),
860
                "dirty": None,
861
                "error": pieces["error"]}
862
863
    if not style or style == "default":
864
        style = "pep440"  # the default
865
866
    if style == "pep440":
867
        rendered = render_pep440(pieces)
868
    elif style == "pep440-pre":
869
        rendered = render_pep440_pre(pieces)
870
    elif style == "pep440-post":
871
        rendered = render_pep440_post(pieces)
872
    elif style == "pep440-old":
873
        rendered = render_pep440_old(pieces)
874
    elif style == "git-describe":
875
        rendered = render_git_describe(pieces)
876
    elif style == "git-describe-long":
877
        rendered = render_git_describe_long(pieces)
878
    else:
879
        raise ValueError("unknown style '%%s'" %% style)
880
881
    return {"version": rendered, "full-revisionid": pieces["long"],
882
            "dirty": pieces["dirty"], "error": None}
883
884
885
def get_versions():
886
    # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
887
    # __file__, we can work backwards from there to the root. Some
888
    # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
889
    # case we can only use expanded keywords.
890
891
    cfg = get_config()
892
    verbose = cfg.verbose
893
894
    try:
895
        return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
896
                                          verbose)
897
    except NotThisMethod:
898
        pass
899
900
    try:
901
        root = os.path.realpath(__file__)
902
        # versionfile_source is the relative path from the top of the source
903
        # tree (where the .git directory might live) to this file. Invert
904
        # this to find the root from __file__.
905
        for i in cfg.versionfile_source.split('/'):
906
            root = os.path.dirname(root)
907
    except NameError:
908
        return {"version": "0+unknown", "full-revisionid": None,
909
                "dirty": None,
910
                "error": "unable to find root of source tree"}
911
912
    try:
913
        pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
914
        return render(pieces, cfg.style)
915
    except NotThisMethod:
916
        pass
917
918
    try:
919
        if cfg.parentdir_prefix:
920
            return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
921
    except NotThisMethod:
922
        pass
923
924
    return {"version": "0+unknown", "full-revisionid": None,
925
            "dirty": None,
926
            "error": "unable to compute version"}
927
'''
928
929
930
@register_vcs_handler("git", "get_keywords")
0 ignored issues
show
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
931
def git_get_keywords(versionfile_abs):
932
    # the code embedded in _version.py can just fetch the value of these
933
    # keywords. When used from setup.py, we don't want to import _version.py,
934
    # so we do it with a regexp instead. This function is not used from
935
    # _version.py.
936
    keywords = {}
937
    try:
938
        f = open(versionfile_abs, "r")
939
        for line in f.readlines():
940
            if line.strip().startswith("git_refnames ="):
941
                mo = re.search(r'=\s*"(.*)"', line)
942
                if mo:
943
                    keywords["refnames"] = mo.group(1)
944
            if line.strip().startswith("git_full ="):
945
                mo = re.search(r'=\s*"(.*)"', line)
946
                if mo:
947
                    keywords["full"] = mo.group(1)
948
        f.close()
949
    except EnvironmentError:
950
        pass
951
    return keywords
952
953
954
@register_vcs_handler("git", "keywords")
0 ignored issues
show
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
955
def git_versions_from_keywords(keywords, tag_prefix, verbose):
956
    if not keywords:
957
        raise NotThisMethod("no keywords at all, weird")
958
    refnames = keywords["refnames"].strip()
959
    if refnames.startswith("$Format"):
960
        if verbose:
961
            print("keywords are unexpanded, not using")
962
        raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
963
    refs = set([r.strip() for r in refnames.strip("()").split(",")])
964
    # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
965
    # just "foo-1.0". If we see a "tag: " prefix, prefer those.
966
    TAG = "tag: "
967
    tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
968
    if not tags:
969
        # Either we're using git < 1.8.3, or there really are no tags. We use
970
        # a heuristic: assume all version tags have a digit. The old git %d
971
        # expansion behaves like git log --decorate=short and strips out the
972
        # refs/heads/ and refs/tags/ prefixes that would let us distinguish
973
        # between branches and tags. By ignoring refnames without digits, we
974
        # filter out many common branch names like "release" and
975
        # "stabilization", as well as "HEAD" and "master".
976
        tags = set([r for r in refs if re.search(r'\d', r)])
977
        if verbose:
978
            print("discarding '%s', no digits" % ",".join(refs-tags))
979
    if verbose:
980
        print("likely tags: %s" % ",".join(sorted(tags)))
981
    for ref in sorted(tags):
982
        # sorting will prefer e.g. "2.0" over "2.0rc1"
983
        if ref.startswith(tag_prefix):
984
            r = ref[len(tag_prefix):]
985
            if verbose:
986
                print("picking %s" % r)
987
            return {"version": r,
988
                    "full-revisionid": keywords["full"].strip(),
989
                    "dirty": False, "error": None
990
                    }
991
    # no suitable tags, so version is "0+unknown", but full hex is still there
992
    if verbose:
993
        print("no suitable tags, using unknown + full revision id")
994
    return {"version": "0+unknown",
995
            "full-revisionid": keywords["full"].strip(),
996
            "dirty": False, "error": "no suitable tags"}
997
998
999
@register_vcs_handler("git", "pieces_from_vcs")
0 ignored issues
show
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1000
def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
1001
    # this runs 'git' from the root of the source tree. This only gets called
1002
    # if the git-archive 'subst' keywords were *not* expanded, and
1003
    # _version.py hasn't already been rewritten with a short version string,
1004
    # meaning we're inside a checked out source tree.
1005
1006
    if not os.path.exists(os.path.join(root, ".git")):
1007
        if verbose:
1008
            print("no .git in %s" % root)
1009
        raise NotThisMethod("no .git directory")
1010
1011
    GITS = ["git"]
1012
    if sys.platform == "win32":
1013
        GITS = ["git.cmd", "git.exe"]
1014
    # if there is a tag, this yields TAG-NUM-gHEX[-dirty]
1015
    # if there are no tags, this yields HEX[-dirty] (no NUM)
1016
    describe_out = run_command(GITS, ["describe", "--tags", "--dirty",
1017
                                      "--always", "--long"],
1018
                               cwd=root)
1019
    # --long was added in git-1.5.5
1020
    if describe_out is None:
1021
        raise NotThisMethod("'git describe' failed")
1022
    describe_out = describe_out.strip()
1023
    full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
1024
    if full_out is None:
1025
        raise NotThisMethod("'git rev-parse' failed")
1026
    full_out = full_out.strip()
1027
1028
    pieces = {}
1029
    pieces["long"] = full_out
1030
    pieces["short"] = full_out[:7]  # maybe improved later
1031
    pieces["error"] = None
1032
1033
    # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
1034
    # TAG might have hyphens.
1035
    git_describe = describe_out
1036
1037
    # look for -dirty suffix
1038
    dirty = git_describe.endswith("-dirty")
1039
    pieces["dirty"] = dirty
1040
    if dirty:
1041
        git_describe = git_describe[:git_describe.rindex("-dirty")]
1042
1043
    # now we have TAG-NUM-gHEX or HEX
1044
1045
    if "-" in git_describe:
1046
        # TAG-NUM-gHEX
1047
        mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
1048
        if not mo:
1049
            # unparseable. Maybe git-describe is misbehaving?
1050
            pieces["error"] = ("unable to parse git-describe output: '%s'"
1051
                               % describe_out)
1052
            return pieces
1053
1054
        # tag
1055
        full_tag = mo.group(1)
1056
        if not full_tag.startswith(tag_prefix):
1057
            if verbose:
1058
                fmt = "tag '%s' doesn't start with prefix '%s'"
1059
                print(fmt % (full_tag, tag_prefix))
1060
            pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
1061
                               % (full_tag, tag_prefix))
1062
            return pieces
1063
        pieces["closest-tag"] = full_tag[len(tag_prefix):]
1064
1065
        # distance: number of commits since tag
1066
        pieces["distance"] = int(mo.group(2))
1067
1068
        # commit: short hex revision ID
1069
        pieces["short"] = mo.group(3)
1070
1071
    else:
1072
        # HEX: no tags
1073
        pieces["closest-tag"] = None
1074
        count_out = run_command(GITS, ["rev-list", "HEAD", "--count"],
1075
                                cwd=root)
1076
        pieces["distance"] = int(count_out)  # total number of commits
1077
1078
    return pieces
1079
1080
1081
def do_vcs_install(manifest_in, versionfile_source, ipy):
1082
    GITS = ["git"]
1083
    if sys.platform == "win32":
1084
        GITS = ["git.cmd", "git.exe"]
1085
    files = [manifest_in, versionfile_source]
1086
    if ipy:
1087
        files.append(ipy)
1088
    try:
1089
        me = __file__
1090
        if me.endswith(".pyc") or me.endswith(".pyo"):
1091
            me = os.path.splitext(me)[0] + ".py"
1092
        versioneer_file = os.path.relpath(me)
1093
    except NameError:
1094
        versioneer_file = "versioneer.py"
1095
    files.append(versioneer_file)
1096
    present = False
1097
    try:
1098
        f = open(".gitattributes", "r")
1099
        for line in f.readlines():
1100
            if line.strip().startswith(versionfile_source):
1101
                if "export-subst" in line.strip().split()[1:]:
1102
                    present = True
1103
        f.close()
1104
    except EnvironmentError:
1105
        pass
1106
    if not present:
1107
        f = open(".gitattributes", "a+")
1108
        f.write("%s export-subst\n" % versionfile_source)
1109
        f.close()
1110
        files.append(".gitattributes")
1111
    run_command(GITS, ["add", "--"] + files)
1112
1113
1114
def versions_from_parentdir(parentdir_prefix, root, verbose):
1115
    # Source tarballs conventionally unpack into a directory that includes
1116
    # both the project name and a version string.
1117
    dirname = os.path.basename(root)
1118
    if not dirname.startswith(parentdir_prefix):
1119
        if verbose:
1120
            print("guessing rootdir is '%s', but '%s' doesn't start with "
1121
                  "prefix '%s'" % (root, dirname, parentdir_prefix))
1122
        raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
1123
    return {"version": dirname[len(parentdir_prefix):],
1124
            "full-revisionid": None,
1125
            "dirty": False, "error": None}
1126
1127
SHORT_VERSION_PY = """
1128
# This file was generated by 'versioneer.py' (0.15) from
1129
# revision-control system data, or from the parent directory name of an
1130
# unpacked source archive. Distribution tarballs contain a pre-generated copy
1131
# of this file.
1132
1133
import json
1134
import sys
1135
1136
version_json = '''
1137
%s
1138
'''  # END VERSION_JSON
1139
1140
1141
def get_versions():
1142
    return json.loads(version_json)
1143
"""
1144
1145
1146
def versions_from_file(filename):
1147
    try:
1148
        with open(filename) as f:
1149
            contents = f.read()
1150
    except EnvironmentError:
1151
        raise NotThisMethod("unable to read _version.py")
1152
    mo = re.search(r"version_json = '''\n(.*)'''  # END VERSION_JSON",
1153
                   contents, re.M | re.S)
1154
    if not mo:
1155
        raise NotThisMethod("no version_json in _version.py")
1156
    return json.loads(mo.group(1))
1157
1158
1159
def write_to_version_file(filename, versions):
1160
    os.unlink(filename)
1161
    contents = json.dumps(versions, sort_keys=True,
1162
                          indent=1, separators=(",", ": "))
1163
    with open(filename, "w") as f:
1164
        f.write(SHORT_VERSION_PY % contents)
1165
1166
    print("set %s to '%s'" % (filename, versions["version"]))
1167
1168
1169
def plus_or_dot(pieces):
1170
    if "+" in pieces.get("closest-tag", ""):
1171
        return "."
1172
    return "+"
1173
1174
1175
def render_pep440(pieces):
0 ignored issues
show
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1176
    # now build up version string, with post-release "local version
1177
    # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
1178
    # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
1179
1180
    # exceptions:
1181
    # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
1182
1183
    if pieces["closest-tag"]:
1184
        rendered = pieces["closest-tag"]
1185
        if pieces["distance"] or pieces["dirty"]:
1186
            rendered += plus_or_dot(pieces)
1187
            rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
1188
            if pieces["dirty"]:
1189
                rendered += ".dirty"
1190
    else:
1191
        # exception #1
1192
        rendered = "0+untagged.%d.g%s" % (pieces["distance"],
1193
                                          pieces["short"])
1194
        if pieces["dirty"]:
1195
            rendered += ".dirty"
1196
    return rendered
1197
1198
1199
def render_pep440_pre(pieces):
1200
    # TAG[.post.devDISTANCE] . No -dirty
1201
1202
    # exceptions:
1203
    # 1: no tags. 0.post.devDISTANCE
1204
1205
    if pieces["closest-tag"]:
1206
        rendered = pieces["closest-tag"]
1207
        if pieces["distance"]:
1208
            rendered += ".post.dev%d" % pieces["distance"]
1209
    else:
1210
        # exception #1
1211
        rendered = "0.post.dev%d" % pieces["distance"]
1212
    return rendered
1213
1214
1215
def render_pep440_post(pieces):
0 ignored issues
show
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1216
    # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that
1217
    # .dev0 sorts backwards (a dirty tree will appear "older" than the
1218
    # corresponding clean one), but you shouldn't be releasing software with
1219
    # -dirty anyways.
1220
1221
    # exceptions:
1222
    # 1: no tags. 0.postDISTANCE[.dev0]
1223
1224
    if pieces["closest-tag"]:
1225
        rendered = pieces["closest-tag"]
1226
        if pieces["distance"] or pieces["dirty"]:
1227
            rendered += ".post%d" % pieces["distance"]
1228
            if pieces["dirty"]:
1229
                rendered += ".dev0"
1230
            rendered += plus_or_dot(pieces)
1231
            rendered += "g%s" % pieces["short"]
1232
    else:
1233
        # exception #1
1234
        rendered = "0.post%d" % pieces["distance"]
1235
        if pieces["dirty"]:
1236
            rendered += ".dev0"
1237
        rendered += "+g%s" % pieces["short"]
1238
    return rendered
1239
1240
1241
def render_pep440_old(pieces):
0 ignored issues
show
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1242
    # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty.
1243
1244
    # exceptions:
1245
    # 1: no tags. 0.postDISTANCE[.dev0]
1246
1247
    if pieces["closest-tag"]:
1248
        rendered = pieces["closest-tag"]
1249
        if pieces["distance"] or pieces["dirty"]:
1250
            rendered += ".post%d" % pieces["distance"]
1251
            if pieces["dirty"]:
1252
                rendered += ".dev0"
1253
    else:
1254
        # exception #1
1255
        rendered = "0.post%d" % pieces["distance"]
1256
        if pieces["dirty"]:
1257
            rendered += ".dev0"
1258
    return rendered
1259
1260
1261
def render_git_describe(pieces):
0 ignored issues
show
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1262
    # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty
1263
    # --always'
1264
1265
    # exceptions:
1266
    # 1: no tags. HEX[-dirty]  (note: no 'g' prefix)
1267
1268
    if pieces["closest-tag"]:
1269
        rendered = pieces["closest-tag"]
1270
        if pieces["distance"]:
1271
            rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
1272
    else:
1273
        # exception #1
1274
        rendered = pieces["short"]
1275
    if pieces["dirty"]:
1276
        rendered += "-dirty"
1277
    return rendered
1278
1279
1280
def render_git_describe_long(pieces):
1281
    # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty
1282
    # --always -long'. The distance/hash is unconditional.
1283
1284
    # exceptions:
1285
    # 1: no tags. HEX[-dirty]  (note: no 'g' prefix)
1286
1287
    if pieces["closest-tag"]:
1288
        rendered = pieces["closest-tag"]
1289
        rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
1290
    else:
1291
        # exception #1
1292
        rendered = pieces["short"]
1293
    if pieces["dirty"]:
1294
        rendered += "-dirty"
1295
    return rendered
1296
1297
1298
def render(pieces, style):
0 ignored issues
show
This code seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1299
    if pieces["error"]:
1300
        return {"version": "unknown",
1301
                "full-revisionid": pieces.get("long"),
1302
                "dirty": None,
1303
                "error": pieces["error"]}
1304
1305
    if not style or style == "default":
1306
        style = "pep440"  # the default
1307
1308
    if style == "pep440":
1309
        rendered = render_pep440(pieces)
1310
    elif style == "pep440-pre":
1311
        rendered = render_pep440_pre(pieces)
1312
    elif style == "pep440-post":
1313
        rendered = render_pep440_post(pieces)
1314
    elif style == "pep440-old":
1315
        rendered = render_pep440_old(pieces)
1316
    elif style == "git-describe":
1317
        rendered = render_git_describe(pieces)
1318
    elif style == "git-describe-long":
1319
        rendered = render_git_describe_long(pieces)
1320
    else:
1321
        raise ValueError("unknown style '%s'" % style)
1322
1323
    return {"version": rendered, "full-revisionid": pieces["long"],
1324
            "dirty": pieces["dirty"], "error": None}
1325
1326
1327
class VersioneerBadRootError(Exception):
1328
    pass
1329
1330
1331
def get_versions(verbose=False):
1332
    # returns dict with two keys: 'version' and 'full'
1333
1334
    if "versioneer" in sys.modules:
1335
        # see the discussion in cmdclass.py:get_cmdclass()
1336
        del sys.modules["versioneer"]
1337
1338
    root = get_root()
1339
    cfg = get_config_from_root(root)
1340
1341
    assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg"
1342
    handlers = HANDLERS.get(cfg.VCS)
1343
    assert handlers, "unrecognized VCS '%s'" % cfg.VCS
1344
    verbose = verbose or cfg.verbose
1345
    assert cfg.versionfile_source is not None, \
1346
        "please set versioneer.versionfile_source"
1347
    assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix"
1348
1349
    versionfile_abs = os.path.join(root, cfg.versionfile_source)
1350
1351
    # extract version from first of: _version.py, VCS command (e.g. 'git
1352
    # describe'), parentdir. This is meant to work for developers using a
1353
    # source checkout, for users of a tarball created by 'setup.py sdist',
1354
    # and for users of a tarball/zipball created by 'git archive' or github's
1355
    # download-from-tag feature or the equivalent in other VCSes.
1356
1357
    get_keywords_f = handlers.get("get_keywords")
1358
    from_keywords_f = handlers.get("keywords")
1359
    if get_keywords_f and from_keywords_f:
1360
        try:
1361
            keywords = get_keywords_f(versionfile_abs)
1362
            ver = from_keywords_f(keywords, cfg.tag_prefix, verbose)
1363
            if verbose:
1364
                print("got version from expanded keyword %s" % ver)
1365
            return ver
1366
        except NotThisMethod:
1367
            pass
1368
1369
    try:
1370
        ver = versions_from_file(versionfile_abs)
1371
        if verbose:
1372
            print("got version from file %s %s" % (versionfile_abs, ver))
1373
        return ver
1374
    except NotThisMethod:
1375
        pass
1376
1377
    from_vcs_f = handlers.get("pieces_from_vcs")
1378
    if from_vcs_f:
1379
        try:
1380
            pieces = from_vcs_f(cfg.tag_prefix, root, verbose)
1381
            ver = render(pieces, cfg.style)
1382
            if verbose:
1383
                print("got version from VCS %s" % ver)
1384
            return ver
1385
        except NotThisMethod:
1386
            pass
1387
1388
    try:
1389
        if cfg.parentdir_prefix:
1390
            ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
1391
            if verbose:
1392
                print("got version from parentdir %s" % ver)
1393
            return ver
1394
    except NotThisMethod:
1395
        pass
1396
1397
    if verbose:
1398
        print("unable to compute version")
1399
1400
    return {"version": "0+unknown", "full-revisionid": None,
1401
            "dirty": None, "error": "unable to compute version"}
1402
1403
1404
def get_version():
1405
    return get_versions()["version"]
1406
1407
1408
def get_cmdclass():
1409
    if "versioneer" in sys.modules:
1410
        del sys.modules["versioneer"]
1411
        # this fixes the "python setup.py develop" case (also 'install' and
1412
        # 'easy_install .'), in which subdependencies of the main project are
1413
        # built (using setup.py bdist_egg) in the same python process. Assume
1414
        # a main project A and a dependency B, which use different versions
1415
        # of Versioneer. A's setup.py imports A's Versioneer, leaving it in
1416
        # sys.modules by the time B's setup.py is executed, causing B to run
1417
        # with the wrong versioneer. Setuptools wraps the sub-dep builds in a
1418
        # sandbox that restores sys.modules to it's pre-build state, so the
1419
        # parent is protected against the child's "import versioneer". By
1420
        # removing ourselves from sys.modules here, before the child build
1421
        # happens, we protect the child from the parent's versioneer too.
1422
        # Also see https://github.com/warner/python-versioneer/issues/52
1423
1424
    cmds = {}
1425
1426
    # we add "version" to both distutils and setuptools
1427
    from distutils.core import Command
1428
1429
    class cmd_version(Command):
1430
        description = "report generated version string"
1431
        user_options = []
1432
        boolean_options = []
1433
1434
        def initialize_options(self):
1435
            pass
1436
1437
        def finalize_options(self):
1438
            pass
1439
1440
        def run(self):
1441
            vers = get_versions(verbose=True)
1442
            print("Version: %s" % vers["version"])
1443
            print(" full-revisionid: %s" % vers.get("full-revisionid"))
1444
            print(" dirty: %s" % vers.get("dirty"))
1445
            if vers["error"]:
1446
                print(" error: %s" % vers["error"])
1447
    cmds["version"] = cmd_version
1448
1449
    # we override "build_py" in both distutils and setuptools
1450
    #
1451
    # most invocation pathways end up running build_py:
1452
    #  distutils/build -> build_py
1453
    #  distutils/install -> distutils/build ->..
1454
    #  setuptools/bdist_wheel -> distutils/install ->..
1455
    #  setuptools/bdist_egg -> distutils/install_lib -> build_py
1456
    #  setuptools/install -> bdist_egg ->..
1457
    #  setuptools/develop -> ?
1458
1459
    from distutils.command.build_py import build_py as _build_py
1460
1461
    class cmd_build_py(_build_py):
1462
        def run(self):
1463
            root = get_root()
1464
            cfg = get_config_from_root(root)
1465
            versions = get_versions()
1466
            _build_py.run(self)
1467
            # now locate _version.py in the new build/ directory and replace
1468
            # it with an updated value
1469
            if cfg.versionfile_build:
1470
                target_versionfile = os.path.join(self.build_lib,
1471
                                                  cfg.versionfile_build)
1472
                print("UPDATING %s" % target_versionfile)
1473
                write_to_version_file(target_versionfile, versions)
1474
    cmds["build_py"] = cmd_build_py
1475
1476
    if "cx_Freeze" in sys.modules:  # cx_freeze enabled?
1477
        from cx_Freeze.dist import build_exe as _build_exe
1478
1479
        class cmd_build_exe(_build_exe):
1480
            def run(self):
1481
                root = get_root()
1482
                cfg = get_config_from_root(root)
1483
                versions = get_versions()
1484
                target_versionfile = cfg.versionfile_source
1485
                print("UPDATING %s" % target_versionfile)
1486
                write_to_version_file(target_versionfile, versions)
1487
1488
                _build_exe.run(self)
1489
                os.unlink(target_versionfile)
1490
                with open(cfg.versionfile_source, "w") as f:
1491
                    LONG = LONG_VERSION_PY[cfg.VCS]
1492
                    f.write(LONG %
1493
                            {"DOLLAR": "$",
1494
                             "STYLE": cfg.style,
1495
                             "TAG_PREFIX": cfg.tag_prefix,
1496
                             "PARENTDIR_PREFIX": cfg.parentdir_prefix,
1497
                             "VERSIONFILE_SOURCE": cfg.versionfile_source,
1498
                             })
1499
        cmds["build_exe"] = cmd_build_exe
1500
        del cmds["build_py"]
1501
1502
    # we override different "sdist" commands for both environments
1503
    if "setuptools" in sys.modules:
1504
        from setuptools.command.sdist import sdist as _sdist
1505
    else:
1506
        from distutils.command.sdist import sdist as _sdist
1507
1508
    class cmd_sdist(_sdist):
1509
        def run(self):
1510
            versions = get_versions()
1511
            self._versioneer_generated_versions = versions
1512
            # unless we update this, the command will keep using the old
1513
            # version
1514
            self.distribution.metadata.version = versions["version"]
1515
            return _sdist.run(self)
1516
1517
        def make_release_tree(self, base_dir, files):
1518
            root = get_root()
1519
            cfg = get_config_from_root(root)
1520
            _sdist.make_release_tree(self, base_dir, files)
1521
            # now locate _version.py in the new base_dir directory
1522
            # (remembering that it may be a hardlink) and replace it with an
1523
            # updated value
1524
            target_versionfile = os.path.join(base_dir, cfg.versionfile_source)
1525
            print("UPDATING %s" % target_versionfile)
1526
            write_to_version_file(target_versionfile,
1527
                                  self._versioneer_generated_versions)
1528
    cmds["sdist"] = cmd_sdist
1529
1530
    return cmds
1531
1532
1533
CONFIG_ERROR = """
1534
setup.cfg is missing the necessary Versioneer configuration. You need
1535
a section like:
1536
1537
 [versioneer]
1538
 VCS = git
1539
 style = pep440
1540
 versionfile_source = src/myproject/_version.py
1541
 versionfile_build = myproject/_version.py
1542
 tag_prefix = ""
1543
 parentdir_prefix = myproject-
1544
1545
You will also need to edit your setup.py to use the results:
1546
1547
 import versioneer
1548
 setup(version=versioneer.get_version(),
1549
       cmdclass=versioneer.get_cmdclass(), ...)
1550
1551
Please read the docstring in ./versioneer.py for configuration instructions,
1552
edit setup.cfg, and re-run the installer or 'python versioneer.py setup'.
1553
"""
1554
1555
SAMPLE_CONFIG = """
1556
# See the docstring in versioneer.py for instructions. Note that you must
1557
# re-run 'versioneer.py setup' after changing this section, and commit the
1558
# resulting files.
1559
1560
[versioneer]
1561
#VCS = git
1562
#style = pep440
1563
#versionfile_source =
1564
#versionfile_build =
1565
#tag_prefix =
1566
#parentdir_prefix =
1567
1568
"""
1569
1570
INIT_PY_SNIPPET = """
1571
from ._version import get_versions
1572
__version__ = get_versions()['version']
1573
del get_versions
1574
"""
1575
1576
1577
def do_setup():
1578
    root = get_root()
1579
    try:
1580
        cfg = get_config_from_root(root)
1581
    except (EnvironmentError, configparser.NoSectionError,
1582
            configparser.NoOptionError) as e:
1583
        if isinstance(e, (EnvironmentError, configparser.NoSectionError)):
1584
            print("Adding sample versioneer config to setup.cfg",
1585
                  file=sys.stderr)
1586
            with open(os.path.join(root, "setup.cfg"), "a") as f:
1587
                f.write(SAMPLE_CONFIG)
1588
        print(CONFIG_ERROR, file=sys.stderr)
1589
        return 1
1590
1591
    print(" creating %s" % cfg.versionfile_source)
1592
    with open(cfg.versionfile_source, "w") as f:
1593
        LONG = LONG_VERSION_PY[cfg.VCS]
1594
        f.write(LONG % {"DOLLAR": "$",
1595
                        "STYLE": cfg.style,
1596
                        "TAG_PREFIX": cfg.tag_prefix,
1597
                        "PARENTDIR_PREFIX": cfg.parentdir_prefix,
1598
                        "VERSIONFILE_SOURCE": cfg.versionfile_source,
1599
                        })
1600
1601
    ipy = os.path.join(os.path.dirname(cfg.versionfile_source),
1602
                       "__init__.py")
1603
    if os.path.exists(ipy):
1604
        try:
1605
            with open(ipy, "r") as f:
1606
                old = f.read()
1607
        except EnvironmentError:
1608
            old = ""
1609
        if INIT_PY_SNIPPET not in old:
1610
            print(" appending to %s" % ipy)
1611
            with open(ipy, "a") as f:
1612
                f.write(INIT_PY_SNIPPET)
1613
        else:
1614
            print(" %s unmodified" % ipy)
1615
    else:
1616
        print(" %s doesn't exist, ok" % ipy)
1617
        ipy = None
1618
1619
    # Make sure both the top-level "versioneer.py" and versionfile_source
1620
    # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so
1621
    # they'll be copied into source distributions. Pip won't be able to
1622
    # install the package without this.
1623
    manifest_in = os.path.join(root, "MANIFEST.in")
1624
    simple_includes = set()
1625
    try:
1626
        with open(manifest_in, "r") as f:
1627
            for line in f:
1628
                if line.startswith("include "):
1629
                    for include in line.split()[1:]:
1630
                        simple_includes.add(include)
1631
    except EnvironmentError:
1632
        pass
1633
    # That doesn't cover everything MANIFEST.in can do
1634
    # (http://docs.python.org/2/distutils/sourcedist.html#commands), so
1635
    # it might give some false negatives. Appending redundant 'include'
1636
    # lines is safe, though.
1637
    if "versioneer.py" not in simple_includes:
1638
        print(" appending 'versioneer.py' to MANIFEST.in")
1639
        with open(manifest_in, "a") as f:
1640
            f.write("include versioneer.py\n")
1641
    else:
1642
        print(" 'versioneer.py' already in MANIFEST.in")
1643
    if cfg.versionfile_source not in simple_includes:
1644
        print(" appending versionfile_source ('%s') to MANIFEST.in" %
1645
              cfg.versionfile_source)
1646
        with open(manifest_in, "a") as f:
1647
            f.write("include %s\n" % cfg.versionfile_source)
1648
    else:
1649
        print(" versionfile_source already in MANIFEST.in")
1650
1651
    # Make VCS-specific changes. For git, this means creating/changing
1652
    # .gitattributes to mark _version.py for export-time keyword
1653
    # substitution.
1654
    do_vcs_install(manifest_in, cfg.versionfile_source, ipy)
1655
    return 0
1656
1657
1658
def scan_setup_py():
1659
    found = set()
1660
    setters = False
1661
    errors = 0
1662
    with open("setup.py", "r") as f:
1663
        for line in f.readlines():
1664
            if "import versioneer" in line:
1665
                found.add("import")
1666
            if "versioneer.get_cmdclass()" in line:
1667
                found.add("cmdclass")
1668
            if "versioneer.get_version()" in line:
1669
                found.add("get_version")
1670
            if "versioneer.VCS" in line:
1671
                setters = True
1672
            if "versioneer.versionfile_source" in line:
1673
                setters = True
1674
    if len(found) != 3:
1675
        print("")
1676
        print("Your setup.py appears to be missing some important items")
1677
        print("(but I might be wrong). Please make sure it has something")
1678
        print("roughly like the following:")
1679
        print("")
1680
        print(" import versioneer")
1681
        print(" setup( version=versioneer.get_version(),")
1682
        print("        cmdclass=versioneer.get_cmdclass(),  ...)")
1683
        print("")
1684
        errors += 1
1685
    if setters:
1686
        print("You should remove lines like 'versioneer.VCS = ' and")
1687
        print("'versioneer.versionfile_source = ' . This configuration")
1688
        print("now lives in setup.cfg, and should be removed from setup.py")
1689
        print("")
1690
        errors += 1
1691
    return errors
1692
1693
if __name__ == "__main__":
1694
    cmd = sys.argv[1]
1695
    if cmd == "setup":
1696
        errors = do_setup()
1697
        errors += scan_setup_py()
1698
        if errors:
1699
            sys.exit(1)
1700