build.versioneer.versions_from_parentdir()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 23
Code Lines 14

Duplication

Lines 23
Ratio 100 %

Importance

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