提交 824294dd authored 作者: Maxim Kochurov's avatar Maxim Kochurov 提交者: Maxim Kochurov

update versioneer script

上级 350f4823
...@@ -29,6 +29,8 @@ import sys ...@@ -29,6 +29,8 @@ import sys
from functools import singledispatch from functools import singledispatch
from typing import Any, NoReturn, Optional from typing import Any, NoReturn, Optional
from pytensor.version import version as __version__
pytensor_logger = logging.getLogger("pytensor") pytensor_logger = logging.getLogger("pytensor")
logging_default_handler = logging.StreamHandler() logging_default_handler = logging.StreamHandler()
...@@ -49,10 +51,6 @@ def disable_log_handler(logger=pytensor_logger, handler=logging_default_handler) ...@@ -49,10 +51,6 @@ def disable_log_handler(logger=pytensor_logger, handler=logging_default_handler)
logger.removeHandler(handler) logger.removeHandler(handler)
# Version information.
from pytensor.version import version as __version__
# Raise a meaningful warning/error if the pytensor directory is in the Python # Raise a meaningful warning/error if the pytensor directory is in the Python
# path. # path.
rpath = os.path.realpath(__path__[0]) rpath = os.path.realpath(__path__[0])
......
# This file helps to compute a version number in source trees obtained from # This file helps to compute a version number in source trees obtained from
# git-archive tarball (such as those provided by githubs download-from-tag # git-archive tarball (such as those provided by githubs download-from-tag
# feature). Distribution tarballs (built by setup.py sdist) and build # feature). Distribution tarballs (built by setup.py sdist) and build
# directories (produced by setup.py build) will contain a much shorter file # directories (produced by setup.py build) will contain a much shorter file
# that just contains the computed version number. # that just contains the computed version number.
# This file is released into the public domain. Generated by # This file is released into the public domain.
# versioneer-0.23.dev0 (https://github.com/python-versioneer/python-versioneer) # Generated by versioneer-0.28
# https://github.com/python-versioneer/python-versioneer
"""Git implementation of _version.py.""" """Git implementation of _version.py."""
import errno import errno
import functools
import os import os
import re import re
import subprocess import subprocess
import sys import sys
from typing import Callable, Dict from typing import Callable, Dict
import functools
def get_keywords(): def get_keywords():
...@@ -59,18 +61,17 @@ HANDLERS: Dict[str, Dict[str, Callable]] = {} ...@@ -59,18 +61,17 @@ HANDLERS: Dict[str, Dict[str, Callable]] = {}
def register_vcs_handler(vcs, method): # decorator def register_vcs_handler(vcs, method): # decorator
"""Create decorator to mark a method as the handler of a VCS.""" """Create decorator to mark a method as the handler of a VCS."""
def decorate(f): def decorate(f):
"""Store f in HANDLERS[vcs][method].""" """Store f in HANDLERS[vcs][method]."""
if vcs not in HANDLERS: if vcs not in HANDLERS:
HANDLERS[vcs] = {} HANDLERS[vcs] = {}
HANDLERS[vcs][method] = f HANDLERS[vcs][method] = f
return f return f
return decorate return decorate
def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
env=None):
"""Call the given command(s).""" """Call the given command(s)."""
assert isinstance(commands, list) assert isinstance(commands, list)
process = None process = None
...@@ -86,14 +87,10 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env= ...@@ -86,14 +87,10 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=
try: try:
dispcmd = str([command] + args) dispcmd = str([command] + args)
# remember shell=False, so use git.cmd on windows, not just git # remember shell=False, so use git.cmd on windows, not just git
process = subprocess.Popen( process = subprocess.Popen([command] + args, cwd=cwd, env=env,
[command] + args, stdout=subprocess.PIPE,
cwd=cwd, stderr=(subprocess.PIPE if hide_stderr
env=env, else None), **popen_kwargs)
stdout=subprocess.PIPE,
stderr=(subprocess.PIPE if hide_stderr else None),
**popen_kwargs
)
break break
except OSError: except OSError:
e = sys.exc_info()[1] e = sys.exc_info()[1]
...@@ -105,7 +102,7 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env= ...@@ -105,7 +102,7 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=
return None, None return None, None
else: else:
if verbose: if verbose:
print(f"unable to find command, tried {commands}") print("unable to find command, tried %s" % (commands,))
return None, None return None, None
stdout = process.communicate()[0].strip().decode() stdout = process.communicate()[0].strip().decode()
if process.returncode != 0: if process.returncode != 0:
...@@ -128,21 +125,15 @@ def versions_from_parentdir(parentdir_prefix, root, verbose): ...@@ -128,21 +125,15 @@ def versions_from_parentdir(parentdir_prefix, root, verbose):
for _ in range(3): for _ in range(3):
dirname = os.path.basename(root) dirname = os.path.basename(root)
if dirname.startswith(parentdir_prefix): if dirname.startswith(parentdir_prefix):
return { return {"version": dirname[len(parentdir_prefix):],
"version": dirname[len(parentdir_prefix) :], "full-revisionid": None,
"full-revisionid": None, "dirty": False, "error": None, "date": None}
"dirty": False,
"error": None,
"date": None,
}
rootdirs.append(root) rootdirs.append(root)
root = os.path.dirname(root) # up a level root = os.path.dirname(root) # up a level
if verbose: if verbose:
print( print("Tried directories %s but none started with prefix %s" %
"Tried directories %s but none started with prefix %s" (str(rootdirs), parentdir_prefix))
% (str(rootdirs), parentdir_prefix)
)
raise NotThisMethod("rootdir doesn't start with parentdir_prefix") raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
...@@ -155,7 +146,7 @@ def git_get_keywords(versionfile_abs): ...@@ -155,7 +146,7 @@ def git_get_keywords(versionfile_abs):
# _version.py. # _version.py.
keywords = {} keywords = {}
try: try:
with open(versionfile_abs) as fobj: with open(versionfile_abs, "r") as fobj:
for line in fobj: for line in fobj:
if line.strip().startswith("git_refnames ="): if line.strip().startswith("git_refnames ="):
mo = re.search(r'=\s*"(.*)"', line) mo = re.search(r'=\s*"(.*)"', line)
...@@ -201,7 +192,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): ...@@ -201,7 +192,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
# starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
# just "foo-1.0". If we see a "tag: " prefix, prefer those. # just "foo-1.0". If we see a "tag: " prefix, prefer those.
TAG = "tag: " TAG = "tag: "
tags = {r[len(TAG) :] for r in refs if r.startswith(TAG)} tags = {r[len(TAG):] for r in refs if r.startswith(TAG)}
if not tags: if not tags:
# Either we're using git < 1.8.3, or there really are no tags. We use # Either we're using git < 1.8.3, or there really are no tags. We use
# a heuristic: assume all version tags have a digit. The old git %d # a heuristic: assume all version tags have a digit. The old git %d
...@@ -210,7 +201,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): ...@@ -210,7 +201,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
# between branches and tags. By ignoring refnames without digits, we # between branches and tags. By ignoring refnames without digits, we
# filter out many common branch names like "release" and # filter out many common branch names like "release" and
# "stabilization", as well as "HEAD" and "master". # "stabilization", as well as "HEAD" and "master".
tags = {r for r in refs if re.search(r"\d", r)} tags = {r for r in refs if re.search(r'\d', r)}
if verbose: if verbose:
print("discarding '%s', no digits" % ",".join(refs - tags)) print("discarding '%s', no digits" % ",".join(refs - tags))
if verbose: if verbose:
...@@ -218,31 +209,24 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): ...@@ -218,31 +209,24 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
for ref in sorted(tags): for ref in sorted(tags):
# sorting will prefer e.g. "2.0" over "2.0rc1" # sorting will prefer e.g. "2.0" over "2.0rc1"
if ref.startswith(tag_prefix): if ref.startswith(tag_prefix):
r = ref[len(tag_prefix) :] r = ref[len(tag_prefix):]
# Filter out refs that exactly match prefix or that don't start # Filter out refs that exactly match prefix or that don't start
# with a number once the prefix is stripped (mostly a concern # with a number once the prefix is stripped (mostly a concern
# when prefix is '') # when prefix is '')
if not re.match(r"\d", r): if not re.match(r'\d', r):
continue continue
if verbose: if verbose:
print("picking %s" % r) print("picking %s" % r)
return { return {"version": r,
"version": r, "full-revisionid": keywords["full"].strip(),
"full-revisionid": keywords["full"].strip(), "dirty": False, "error": None,
"dirty": False, "date": date}
"error": None,
"date": date,
}
# no suitable tags, so version is "0+unknown", but full hex is still there # no suitable tags, so version is "0+unknown", but full hex is still there
if verbose: if verbose:
print("no suitable tags, using unknown + full revision id") print("no suitable tags, using unknown + full revision id")
return { return {"version": "0+unknown",
"version": "0+unknown", "full-revisionid": keywords["full"].strip(),
"full-revisionid": keywords["full"].strip(), "dirty": False, "error": "no suitable tags", "date": None}
"dirty": False,
"error": "no suitable tags",
"date": None,
}
@register_vcs_handler("git", "pieces_from_vcs") @register_vcs_handler("git", "pieces_from_vcs")
...@@ -264,21 +248,19 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -264,21 +248,19 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
env.pop("GIT_DIR", None) env.pop("GIT_DIR", None)
runner = functools.partial(runner, env=env) runner = functools.partial(runner, env=env)
_, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
hide_stderr=not verbose)
if rc != 0: if rc != 0:
if verbose: if verbose:
print("Directory %s not under git control" % root) print("Directory %s not under git control" % root)
raise NotThisMethod("'git rev-parse --git-dir' returned error") raise NotThisMethod("'git rev-parse --git-dir' returned error")
MATCH_ARGS = ["--match", "%s*" % tag_prefix] if tag_prefix else []
# if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
# if there isn't one, this yields HEX[-dirty] (no NUM) # if there isn't one, this yields HEX[-dirty] (no NUM)
describe_out, rc = runner( describe_out, rc = runner(GITS, [
GITS, "describe", "--tags", "--dirty", "--always", "--long",
["describe", "--tags", "--dirty", "--always", "--long", *MATCH_ARGS], "--match", f"{tag_prefix}[[:digit:]]*"
cwd=root, ], cwd=root)
)
# --long was added in git-1.5.5 # --long was added in git-1.5.5
if describe_out is None: if describe_out is None:
raise NotThisMethod("'git describe' failed") raise NotThisMethod("'git describe' failed")
...@@ -293,7 +275,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -293,7 +275,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
pieces["short"] = full_out[:7] # maybe improved later pieces["short"] = full_out[:7] # maybe improved later
pieces["error"] = None pieces["error"] = None
branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"], cwd=root) branch_name, rc = runner(GITS, ["rev-parse", "--abbrev-ref", "HEAD"],
cwd=root)
# --abbrev-ref was added in git-1.6.3 # --abbrev-ref was added in git-1.6.3
if rc != 0 or branch_name is None: if rc != 0 or branch_name is None:
raise NotThisMethod("'git rev-parse --abbrev-ref' returned error") raise NotThisMethod("'git rev-parse --abbrev-ref' returned error")
...@@ -333,16 +316,17 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -333,16 +316,17 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
dirty = git_describe.endswith("-dirty") dirty = git_describe.endswith("-dirty")
pieces["dirty"] = dirty pieces["dirty"] = dirty
if dirty: if dirty:
git_describe = git_describe[: git_describe.rindex("-dirty")] git_describe = git_describe[:git_describe.rindex("-dirty")]
# now we have TAG-NUM-gHEX or HEX # now we have TAG-NUM-gHEX or HEX
if "-" in git_describe: if "-" in git_describe:
# TAG-NUM-gHEX # TAG-NUM-gHEX
mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
if not mo: if not mo:
# unparsable. Maybe git-describe is misbehaving? # unparsable. Maybe git-describe is misbehaving?
pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out pieces["error"] = ("unable to parse git-describe output: '%s'"
% describe_out)
return pieces return pieces
# tag # tag
...@@ -351,12 +335,10 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -351,12 +335,10 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
if verbose: if verbose:
fmt = "tag '%s' doesn't start with prefix '%s'" fmt = "tag '%s' doesn't start with prefix '%s'"
print(fmt % (full_tag, tag_prefix)) print(fmt % (full_tag, tag_prefix))
pieces["error"] = "tag '{}' doesn't start with prefix '{}'".format( pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
full_tag, % (full_tag, tag_prefix))
tag_prefix,
)
return pieces return pieces
pieces["closest-tag"] = full_tag[len(tag_prefix) :] pieces["closest-tag"] = full_tag[len(tag_prefix):]
# distance: number of commits since tag # distance: number of commits since tag
pieces["distance"] = int(mo.group(2)) pieces["distance"] = int(mo.group(2))
...@@ -367,8 +349,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -367,8 +349,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
else: else:
# HEX: no tags # HEX: no tags
pieces["closest-tag"] = None pieces["closest-tag"] = None
count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root) out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root)
pieces["distance"] = int(count_out) # total number of commits pieces["distance"] = len(out.split()) # total number of commits
# commit date: see ISO-8601 comment in git_versions_from_keywords() # commit date: see ISO-8601 comment in git_versions_from_keywords()
date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip()
...@@ -405,7 +387,8 @@ def render_pep440(pieces): ...@@ -405,7 +387,8 @@ def render_pep440(pieces):
rendered += ".dirty" rendered += ".dirty"
else: else:
# exception #1 # exception #1
rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) rendered = "0+untagged.%d.g%s" % (pieces["distance"],
pieces["short"])
if pieces["dirty"]: if pieces["dirty"]:
rendered += ".dirty" rendered += ".dirty"
return rendered return rendered
...@@ -434,7 +417,8 @@ def render_pep440_branch(pieces): ...@@ -434,7 +417,8 @@ def render_pep440_branch(pieces):
rendered = "0" rendered = "0"
if pieces["branch"] != "master": if pieces["branch"] != "master":
rendered += ".dev0" rendered += ".dev0"
rendered += "+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) rendered += "+untagged.%d.g%s" % (pieces["distance"],
pieces["short"])
if pieces["dirty"]: if pieces["dirty"]:
rendered += ".dirty" rendered += ".dirty"
return rendered return rendered
...@@ -595,13 +579,11 @@ def render_git_describe_long(pieces): ...@@ -595,13 +579,11 @@ def render_git_describe_long(pieces):
def render(pieces, style): def render(pieces, style):
"""Render the given version pieces into the requested style.""" """Render the given version pieces into the requested style."""
if pieces["error"]: if pieces["error"]:
return { return {"version": "unknown",
"version": "unknown", "full-revisionid": pieces.get("long"),
"full-revisionid": pieces.get("long"), "dirty": None,
"dirty": None, "error": pieces["error"],
"error": pieces["error"], "date": None}
"date": None,
}
if not style or style == "default": if not style or style == "default":
style = "pep440" # the default style = "pep440" # the default
...@@ -625,13 +607,9 @@ def render(pieces, style): ...@@ -625,13 +607,9 @@ def render(pieces, style):
else: else:
raise ValueError("unknown style '%s'" % style) raise ValueError("unknown style '%s'" % style)
return { return {"version": rendered, "full-revisionid": pieces["long"],
"version": rendered, "dirty": pieces["dirty"], "error": None,
"full-revisionid": pieces["long"], "date": pieces.get("date")}
"dirty": pieces["dirty"],
"error": None,
"date": pieces.get("date"),
}
def get_versions(): def get_versions():
...@@ -645,7 +623,8 @@ def get_versions(): ...@@ -645,7 +623,8 @@ def get_versions():
verbose = cfg.verbose verbose = cfg.verbose
try: try:
return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
verbose)
except NotThisMethod: except NotThisMethod:
pass pass
...@@ -654,16 +633,13 @@ def get_versions(): ...@@ -654,16 +633,13 @@ def get_versions():
# versionfile_source is the relative path from the top of the source # versionfile_source is the relative path from the top of the source
# tree (where the .git directory might live) to this file. Invert # tree (where the .git directory might live) to this file. Invert
# this to find the root from __file__. # this to find the root from __file__.
for _ in cfg.versionfile_source.split("/"): for _ in cfg.versionfile_source.split('/'):
root = os.path.dirname(root) root = os.path.dirname(root)
except NameError: except NameError:
return { return {"version": "0+unknown", "full-revisionid": None,
"version": "0+unknown", "dirty": None,
"full-revisionid": None, "error": "unable to find root of source tree",
"dirty": None, "date": None}
"error": "unable to find root of source tree",
"date": None,
}
try: try:
pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
...@@ -677,10 +653,6 @@ def get_versions(): ...@@ -677,10 +653,6 @@ def get_versions():
except NotThisMethod: except NotThisMethod:
pass pass
return { return {"version": "0+unknown", "full-revisionid": None,
"version": "0+unknown", "dirty": None,
"full-revisionid": None, "error": "unable to compute version", "date": None}
"dirty": None,
"error": "unable to compute version",
"date": None,
}
# Version: 0.23.dev0
# Version: 0.28
"""The Versioneer - like a rocketeer, but for versions. """The Versioneer - like a rocketeer, but for versions.
...@@ -8,12 +9,12 @@ The Versioneer ...@@ -8,12 +9,12 @@ The Versioneer
* like a rocketeer, but for versions! * like a rocketeer, but for versions!
* https://github.com/python-versioneer/python-versioneer * https://github.com/python-versioneer/python-versioneer
* Brian Warner * Brian Warner
* License: Public Domain (CC0-1.0) * License: Public Domain (Unlicense)
* Compatible with: Python 3.7, 3.8, 3.9, 3.10 and pypy3 * Compatible with: Python 3.7, 3.8, 3.9, 3.10 and pypy3
* [![Latest Version][pypi-image]][pypi-url] * [![Latest Version][pypi-image]][pypi-url]
* [![Build Status][travis-image]][travis-url] * [![Build Status][travis-image]][travis-url]
This is a tool for managing a recorded version number in distutils/setuptools-based This is a tool for managing a recorded version number in setuptools-based
python projects. The goal is to remove the tedious and error-prone "update python projects. The goal is to remove the tedious and error-prone "update
the embedded version string" step from your release process. Making a new the embedded version string" step from your release process. Making a new
release should be as easy as recording a new tag in your version-control release should be as easy as recording a new tag in your version-control
...@@ -22,10 +23,38 @@ system, and maybe making new tarballs. ...@@ -22,10 +23,38 @@ system, and maybe making new tarballs.
## Quick Install ## Quick Install
Versioneer provides two installation modes. The "classic" vendored mode installs
a copy of versioneer into your repository. The experimental build-time dependency mode
is intended to allow you to skip this step and simplify the process of upgrading.
### Vendored mode
* `pip install versioneer` to somewhere in your $PATH * `pip install versioneer` to somewhere in your $PATH
* add a `[versioneer]` section to your setup.cfg (see [Install](INSTALL.md)) * A [conda-forge recipe](https://github.com/conda-forge/versioneer-feedstock) is
* run `versioneer install` in your source tree, commit the results available, so you can also use `conda install -c conda-forge versioneer`
* Verify version information with `python setup.py version` * add a `[tool.versioneer]` section to your `pyproject.toml` or a
`[versioneer]` section to your `setup.cfg` (see [Install](INSTALL.md))
* Note that you will need to add `tomli; python_version < "3.11"` to your
build-time dependencies if you use `pyproject.toml`
* run `versioneer install --vendor` in your source tree, commit the results
* verify version information with `python setup.py version`
### Build-time dependency mode
* `pip install versioneer` to somewhere in your $PATH
* A [conda-forge recipe](https://github.com/conda-forge/versioneer-feedstock) is
available, so you can also use `conda install -c conda-forge versioneer`
* add a `[tool.versioneer]` section to your `pyproject.toml` or a
`[versioneer]` section to your `setup.cfg` (see [Install](INSTALL.md))
* add `versioneer` (with `[toml]` extra, if configuring in `pyproject.toml`)
to the `requires` key of the `build-system` table in `pyproject.toml`:
```toml
[build-system]
requires = ["setuptools", "versioneer[toml]"]
build-backend = "setuptools.build_meta"
```
* run `versioneer install --no-vendor` in your source tree, commit the results
* verify version information with `python setup.py version`
## Version Identifiers ## Version Identifiers
...@@ -230,9 +259,10 @@ resolve it. ...@@ -230,9 +259,10 @@ resolve it.
To upgrade your project to a new release of Versioneer, do the following: To upgrade your project to a new release of Versioneer, do the following:
* install the new Versioneer (`pip install -U versioneer` or equivalent) * install the new Versioneer (`pip install -U versioneer` or equivalent)
* edit `setup.cfg`, if necessary, to include any new configuration settings * edit `setup.cfg` and `pyproject.toml`, if necessary,
indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. to include any new configuration settings indicated by the release notes.
* re-run `versioneer install` in your source tree, to replace See [UPGRADING](./UPGRADING.md) for details.
* re-run `versioneer install --[no-]vendor` in your source tree, to replace
`SRC/_version.py` `SRC/_version.py`
* commit any changed files * commit any changed files
...@@ -262,9 +292,8 @@ number of intermediate scripts. ...@@ -262,9 +292,8 @@ number of intermediate scripts.
To make Versioneer easier to embed, all its code is dedicated to the public To make Versioneer easier to embed, all its code is dedicated to the public
domain. The `_version.py` that it creates is also in the public domain. domain. The `_version.py` that it creates is also in the public domain.
Specifically, both are released under the Creative Commons "Public Domain Specifically, both are released under the "Unlicense", as described in
Dedication" license (CC0-1.0), as described in https://unlicense.org/.
https://creativecommons.org/publicdomain/zero/1.0/ .
[pypi-image]: https://img.shields.io/pypi/v/versioneer.svg [pypi-image]: https://img.shields.io/pypi/v/versioneer.svg
[pypi-url]: https://pypi.python.org/pypi/versioneer/ [pypi-url]: https://pypi.python.org/pypi/versioneer/
...@@ -286,9 +315,19 @@ import os ...@@ -286,9 +315,19 @@ import os
import re import re
import subprocess import subprocess
import sys import sys
from pathlib import Path
from typing import Callable, Dict from typing import Callable, Dict
import functools import functools
have_tomllib = True
if sys.version_info >= (3, 11):
import tomllib
else:
try:
import tomli as tomllib
except ImportError:
have_tomllib = False
class VersioneerConfig: class VersioneerConfig:
"""Container for Versioneer configuration parameters.""" """Container for Versioneer configuration parameters."""
...@@ -325,7 +364,7 @@ def get_root(): ...@@ -325,7 +364,7 @@ def get_root():
my_path = os.path.realpath(os.path.abspath(__file__)) my_path = os.path.realpath(os.path.abspath(__file__))
me_dir = os.path.normcase(os.path.splitext(my_path)[0]) me_dir = os.path.normcase(os.path.splitext(my_path)[0])
vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0])
if me_dir != vsr_dir: if me_dir != vsr_dir and "VERSIONEER_PEP518" not in globals():
print("Warning: build in %s is using versioneer.py from %s" print("Warning: build in %s is using versioneer.py from %s"
% (os.path.dirname(my_path), versioneer_py)) % (os.path.dirname(my_path), versioneer_py))
except NameError: except NameError:
...@@ -339,22 +378,32 @@ def get_config_from_root(root): ...@@ -339,22 +378,32 @@ def get_config_from_root(root):
# configparser.NoSectionError (if it lacks a [versioneer] section), or # configparser.NoSectionError (if it lacks a [versioneer] section), or
# configparser.NoOptionError (if it lacks "VCS="). See the docstring at # configparser.NoOptionError (if it lacks "VCS="). See the docstring at
# the top of versioneer.py for instructions on writing your setup.cfg . # the top of versioneer.py for instructions on writing your setup.cfg .
setup_cfg = os.path.join(root, "setup.cfg") root = Path(root)
parser = configparser.ConfigParser() pyproject_toml = root / "pyproject.toml"
with open(setup_cfg) as cfg_file: setup_cfg = root / "setup.cfg"
parser.read_file(cfg_file) section = None
VCS = parser.get("versioneer", "VCS") # mandatory if pyproject_toml.exists() and have_tomllib:
try:
with open(pyproject_toml, 'rb') as fobj:
pp = tomllib.load(fobj)
section = pp['tool']['versioneer']
except (tomllib.TOMLDecodeError, KeyError):
pass
if not section:
parser = configparser.ConfigParser()
with open(setup_cfg) as cfg_file:
parser.read_file(cfg_file)
parser.get("versioneer", "VCS") # raise error if missing
# Dict-like interface for non-mandatory entries section = parser["versioneer"]
section = parser["versioneer"]
cfg = VersioneerConfig() cfg = VersioneerConfig()
cfg.VCS = VCS cfg.VCS = section['VCS']
cfg.style = section.get("style", "") cfg.style = section.get("style", "")
cfg.versionfile_source = section.get("versionfile_source") cfg.versionfile_source = section.get("versionfile_source")
cfg.versionfile_build = section.get("versionfile_build") cfg.versionfile_build = section.get("versionfile_build")
cfg.tag_prefix = section.get("tag_prefix") cfg.tag_prefix = section.get("tag_prefix")
if cfg.tag_prefix in ("''", '""'): if cfg.tag_prefix in ("''", '""', None):
cfg.tag_prefix = "" cfg.tag_prefix = ""
cfg.parentdir_prefix = section.get("parentdir_prefix") cfg.parentdir_prefix = section.get("parentdir_prefix")
cfg.verbose = section.get("verbose") cfg.verbose = section.get("verbose")
...@@ -411,7 +460,7 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, ...@@ -411,7 +460,7 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
return None, None return None, None
else: else:
if verbose: if verbose:
print(f"unable to find command, tried {commands}") print("unable to find command, tried %s" % (commands,))
return None, None return None, None
stdout = process.communicate()[0].strip().decode() stdout = process.communicate()[0].strip().decode()
if process.returncode != 0: if process.returncode != 0:
...@@ -429,8 +478,9 @@ LONG_VERSION_PY['git'] = r''' ...@@ -429,8 +478,9 @@ LONG_VERSION_PY['git'] = r'''
# directories (produced by setup.py build) will contain a much shorter file # directories (produced by setup.py build) will contain a much shorter file
# that just contains the computed version number. # that just contains the computed version number.
# This file is released into the public domain. Generated by # This file is released into the public domain.
# versioneer-0.23.dev0 (https://github.com/python-versioneer/python-versioneer) # Generated by versioneer-0.28
# https://github.com/python-versioneer/python-versioneer
"""Git implementation of _version.py.""" """Git implementation of _version.py."""
...@@ -672,19 +722,18 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -672,19 +722,18 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
runner = functools.partial(runner, env=env) runner = functools.partial(runner, env=env)
_, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
hide_stderr=True) hide_stderr=not verbose)
if rc != 0: if rc != 0:
if verbose: if verbose:
print("Directory %%s not under git control" %% root) print("Directory %%s not under git control" %% root)
raise NotThisMethod("'git rev-parse --git-dir' returned error") raise NotThisMethod("'git rev-parse --git-dir' returned error")
MATCH_ARGS = ["--match", "%%s*" %% tag_prefix] if tag_prefix else []
# if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
# if there isn't one, this yields HEX[-dirty] (no NUM) # if there isn't one, this yields HEX[-dirty] (no NUM)
describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty", describe_out, rc = runner(GITS, [
"--always", "--long", *MATCH_ARGS], "describe", "--tags", "--dirty", "--always", "--long",
cwd=root) "--match", f"{tag_prefix}[[:digit:]]*"
], cwd=root)
# --long was added in git-1.5.5 # --long was added in git-1.5.5
if describe_out is None: if describe_out is None:
raise NotThisMethod("'git describe' failed") raise NotThisMethod("'git describe' failed")
...@@ -773,8 +822,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -773,8 +822,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
else: else:
# HEX: no tags # HEX: no tags
pieces["closest-tag"] = None pieces["closest-tag"] = None
count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root) out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root)
pieces["distance"] = int(count_out) # total number of commits pieces["distance"] = len(out.split()) # total number of commits
# commit date: see ISO-8601 comment in git_versions_from_keywords() # commit date: see ISO-8601 comment in git_versions_from_keywords()
date = runner(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip() date = runner(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip()
...@@ -870,7 +919,7 @@ def render_pep440_pre(pieces): ...@@ -870,7 +919,7 @@ def render_pep440_pre(pieces):
tag_version, post_version = pep440_split_post(pieces["closest-tag"]) tag_version, post_version = pep440_split_post(pieces["closest-tag"])
rendered = tag_version rendered = tag_version
if post_version is not None: if post_version is not None:
rendered += ".post%%d.dev%%d" %% (post_version+1, pieces["distance"]) rendered += ".post%%d.dev%%d" %% (post_version + 1, pieces["distance"])
else: else:
rendered += ".post0.dev%%d" %% (pieces["distance"]) rendered += ".post0.dev%%d" %% (pieces["distance"])
else: else:
...@@ -1092,7 +1141,7 @@ def git_get_keywords(versionfile_abs): ...@@ -1092,7 +1141,7 @@ def git_get_keywords(versionfile_abs):
# _version.py. # _version.py.
keywords = {} keywords = {}
try: try:
with open(versionfile_abs) as fobj: with open(versionfile_abs, "r") as fobj:
for line in fobj: for line in fobj:
if line.strip().startswith("git_refnames ="): if line.strip().startswith("git_refnames ="):
mo = re.search(r'=\s*"(.*)"', line) mo = re.search(r'=\s*"(.*)"', line)
...@@ -1195,19 +1244,18 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -1195,19 +1244,18 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
runner = functools.partial(runner, env=env) runner = functools.partial(runner, env=env)
_, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root, _, rc = runner(GITS, ["rev-parse", "--git-dir"], cwd=root,
hide_stderr=True) hide_stderr=not verbose)
if rc != 0: if rc != 0:
if verbose: if verbose:
print("Directory %s not under git control" % root) print("Directory %s not under git control" % root)
raise NotThisMethod("'git rev-parse --git-dir' returned error") raise NotThisMethod("'git rev-parse --git-dir' returned error")
MATCH_ARGS = ["--match", "%s*" % tag_prefix] if tag_prefix else []
# if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
# if there isn't one, this yields HEX[-dirty] (no NUM) # if there isn't one, this yields HEX[-dirty] (no NUM)
describe_out, rc = runner(GITS, ["describe", "--tags", "--dirty", describe_out, rc = runner(GITS, [
"--always", "--long", *MATCH_ARGS], "describe", "--tags", "--dirty", "--always", "--long",
cwd=root) "--match", f"{tag_prefix}[[:digit:]]*"
], cwd=root)
# --long was added in git-1.5.5 # --long was added in git-1.5.5
if describe_out is None: if describe_out is None:
raise NotThisMethod("'git describe' failed") raise NotThisMethod("'git describe' failed")
...@@ -1296,8 +1344,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -1296,8 +1344,8 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
else: else:
# HEX: no tags # HEX: no tags
pieces["closest-tag"] = None pieces["closest-tag"] = None
count_out, rc = runner(GITS, ["rev-list", "HEAD", "--count"], cwd=root) out, rc = runner(GITS, ["rev-list", "HEAD", "--left-right"], cwd=root)
pieces["distance"] = int(count_out) # total number of commits pieces["distance"] = len(out.split()) # total number of commits
# commit date: see ISO-8601 comment in git_versions_from_keywords() # commit date: see ISO-8601 comment in git_versions_from_keywords()
date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip()
...@@ -1309,7 +1357,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): ...@@ -1309,7 +1357,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command):
return pieces return pieces
def do_vcs_install(manifest_in, versionfile_source, ipy): def do_vcs_install(versionfile_source, ipy):
"""Git-specific installation logic for Versioneer. """Git-specific installation logic for Versioneer.
For Git, this means creating/changing .gitattributes to mark _version.py For Git, this means creating/changing .gitattributes to mark _version.py
...@@ -1318,20 +1366,21 @@ def do_vcs_install(manifest_in, versionfile_source, ipy): ...@@ -1318,20 +1366,21 @@ def do_vcs_install(manifest_in, versionfile_source, ipy):
GITS = ["git"] GITS = ["git"]
if sys.platform == "win32": if sys.platform == "win32":
GITS = ["git.cmd", "git.exe"] GITS = ["git.cmd", "git.exe"]
files = [manifest_in, versionfile_source] files = [versionfile_source]
if ipy: if ipy:
files.append(ipy) files.append(ipy)
try: if "VERSIONEER_PEP518" not in globals():
my_path = __file__ try:
if my_path.endswith(".pyc") or my_path.endswith(".pyo"): my_path = __file__
my_path = os.path.splitext(my_path)[0] + ".py" if my_path.endswith((".pyc", ".pyo")):
versioneer_file = os.path.relpath(my_path) my_path = os.path.splitext(my_path)[0] + ".py"
except NameError: versioneer_file = os.path.relpath(my_path)
versioneer_file = "versioneer.py" except NameError:
files.append(versioneer_file) versioneer_file = "versioneer.py"
files.append(versioneer_file)
present = False present = False
try: try:
with open(".gitattributes") as fobj: with open(".gitattributes", "r") as fobj:
for line in fobj: for line in fobj:
if line.strip().startswith(versionfile_source): if line.strip().startswith(versionfile_source):
if "export-subst" in line.strip().split()[1:]: if "export-subst" in line.strip().split()[1:]:
...@@ -1371,7 +1420,7 @@ def versions_from_parentdir(parentdir_prefix, root, verbose): ...@@ -1371,7 +1420,7 @@ def versions_from_parentdir(parentdir_prefix, root, verbose):
SHORT_VERSION_PY = """ SHORT_VERSION_PY = """
# This file was generated by 'versioneer.py' (0.23.dev0) from # This file was generated by 'versioneer.py' (0.28) from
# revision-control system data, or from the parent directory name of an # revision-control system data, or from the parent directory name of an
# unpacked source archive. Distribution tarballs contain a pre-generated copy # unpacked source archive. Distribution tarballs contain a pre-generated copy
# of this file. # of this file.
...@@ -1413,7 +1462,7 @@ def write_to_version_file(filename, versions): ...@@ -1413,7 +1462,7 @@ def write_to_version_file(filename, versions):
with open(filename, "w") as f: with open(filename, "w") as f:
f.write(SHORT_VERSION_PY % contents) f.write(SHORT_VERSION_PY % contents)
print("set {} to '{}'".format(filename, versions["version"])) print("set %s to '%s'" % (filename, versions["version"]))
def plus_or_dot(pieces): def plus_or_dot(pieces):
...@@ -1500,7 +1549,7 @@ def render_pep440_pre(pieces): ...@@ -1500,7 +1549,7 @@ def render_pep440_pre(pieces):
tag_version, post_version = pep440_split_post(pieces["closest-tag"]) tag_version, post_version = pep440_split_post(pieces["closest-tag"])
rendered = tag_version rendered = tag_version
if post_version is not None: if post_version is not None:
rendered += ".post%d.dev%d" % (post_version+1, pieces["distance"]) rendered += ".post%d.dev%d" % (post_version + 1, pieces["distance"])
else: else:
rendered += ".post0.dev%d" % (pieces["distance"]) rendered += ".post0.dev%d" % (pieces["distance"])
else: else:
...@@ -1713,7 +1762,7 @@ def get_versions(verbose=False): ...@@ -1713,7 +1762,7 @@ def get_versions(verbose=False):
try: try:
ver = versions_from_file(versionfile_abs) ver = versions_from_file(versionfile_abs)
if verbose: if verbose:
print(f"got version from file {versionfile_abs} {ver}") print("got version from file %s %s" % (versionfile_abs, ver))
return ver return ver
except NotThisMethod: except NotThisMethod:
pass pass
...@@ -1813,6 +1862,9 @@ def get_cmdclass(cmdclass=None): ...@@ -1813,6 +1862,9 @@ def get_cmdclass(cmdclass=None):
# then does setup.py bdist_wheel, or sometimes setup.py install # then does setup.py bdist_wheel, or sometimes setup.py install
# setup.py egg_info -> ? # setup.py egg_info -> ?
# pip install -e . and setuptool/editable_wheel will invoke build_py
# but the build_py command is not expected to copy any files.
# we override different "build_py" commands for both environments # we override different "build_py" commands for both environments
if 'build_py' in cmds: if 'build_py' in cmds:
_build_py = cmds['build_py'] _build_py = cmds['build_py']
...@@ -1825,6 +1877,10 @@ def get_cmdclass(cmdclass=None): ...@@ -1825,6 +1877,10 @@ def get_cmdclass(cmdclass=None):
cfg = get_config_from_root(root) cfg = get_config_from_root(root)
versions = get_versions() versions = get_versions()
_build_py.run(self) _build_py.run(self)
if getattr(self, "editable_mode", False):
# During editable installs `.py` and data files are
# not copied to build_lib
return
# now locate _version.py in the new build/ directory and replace # now locate _version.py in the new build/ directory and replace
# it with an updated value # it with an updated value
if cfg.versionfile_build: if cfg.versionfile_build:
...@@ -1853,6 +1909,8 @@ def get_cmdclass(cmdclass=None): ...@@ -1853,6 +1909,8 @@ def get_cmdclass(cmdclass=None):
return return
# now locate _version.py in the new build/ directory and replace # now locate _version.py in the new build/ directory and replace
# it with an updated value # it with an updated value
if not cfg.versionfile_build:
return
target_versionfile = os.path.join(self.build_lib, target_versionfile = os.path.join(self.build_lib,
cfg.versionfile_build) cfg.versionfile_build)
if not os.path.exists(target_versionfile): if not os.path.exists(target_versionfile):
...@@ -1897,7 +1955,10 @@ def get_cmdclass(cmdclass=None): ...@@ -1897,7 +1955,10 @@ def get_cmdclass(cmdclass=None):
del cmds["build_py"] del cmds["build_py"]
if 'py2exe' in sys.modules: # py2exe enabled? if 'py2exe' in sys.modules: # py2exe enabled?
from py2exe.distutils_buildexe import py2exe as _py2exe try:
from py2exe.setuptools_buildexe import py2exe as _py2exe
except ImportError:
from py2exe.distutils_buildexe import py2exe as _py2exe
class cmd_py2exe(_py2exe): class cmd_py2exe(_py2exe):
def run(self): def run(self):
...@@ -1921,6 +1982,43 @@ def get_cmdclass(cmdclass=None): ...@@ -1921,6 +1982,43 @@ def get_cmdclass(cmdclass=None):
}) })
cmds["py2exe"] = cmd_py2exe cmds["py2exe"] = cmd_py2exe
# sdist farms its file list building out to egg_info
if 'egg_info' in cmds:
_egg_info = cmds['egg_info']
else:
from setuptools.command.egg_info import egg_info as _egg_info
class cmd_egg_info(_egg_info):
def find_sources(self):
# egg_info.find_sources builds the manifest list and writes it
# in one shot
super().find_sources()
# Modify the filelist and normalize it
root = get_root()
cfg = get_config_from_root(root)
self.filelist.append('versioneer.py')
if cfg.versionfile_source:
# There are rare cases where versionfile_source might not be
# included by default, so we must be explicit
self.filelist.append(cfg.versionfile_source)
self.filelist.sort()
self.filelist.remove_duplicates()
# The write method is hidden in the manifest_maker instance that
# generated the filelist and was thrown away
# We will instead replicate their final normalization (to unicode,
# and POSIX-style paths)
from setuptools import unicode_utils
normalized = [unicode_utils.filesys_decode(f).replace(os.sep, '/')
for f in self.filelist.files]
manifest_filename = os.path.join(self.egg_info, 'SOURCES.txt')
with open(manifest_filename, 'w') as fobj:
fobj.write('\n'.join(normalized))
cmds['egg_info'] = cmd_egg_info
# we override different "sdist" commands for both environments # we override different "sdist" commands for both environments
if 'sdist' in cmds: if 'sdist' in cmds:
_sdist = cmds['sdist'] _sdist = cmds['sdist']
...@@ -2030,7 +2128,7 @@ def do_setup(): ...@@ -2030,7 +2128,7 @@ def do_setup():
"__init__.py") "__init__.py")
if os.path.exists(ipy): if os.path.exists(ipy):
try: try:
with open(ipy) as f: with open(ipy, "r") as f:
old = f.read() old = f.read()
except OSError: except OSError:
old = "" old = ""
...@@ -2050,42 +2148,10 @@ def do_setup(): ...@@ -2050,42 +2148,10 @@ def do_setup():
print(" %s doesn't exist, ok" % ipy) print(" %s doesn't exist, ok" % ipy)
ipy = None ipy = None
# Make sure both the top-level "versioneer.py" and versionfile_source
# (PKG/_version.py, used by runtime code) are in MANIFEST.in, so
# they'll be copied into source distributions. Pip won't be able to
# install the package without this.
manifest_in = os.path.join(root, "MANIFEST.in")
simple_includes = set()
try:
with open(manifest_in) as f:
for line in f:
if line.startswith("include "):
for include in line.split()[1:]:
simple_includes.add(include)
except OSError:
pass
# That doesn't cover everything MANIFEST.in can do
# (https://packaging.python.org/en/latest/guides/using-manifest-in/#manifest-in-commands),
# so it might give some false negatives. Appending redundant 'include'
# lines is safe, though.
if "versioneer.py" not in simple_includes:
print(" appending 'versioneer.py' to MANIFEST.in")
with open(manifest_in, "a") as f:
f.write("include versioneer.py\n")
else:
print(" 'versioneer.py' already in MANIFEST.in")
if cfg.versionfile_source not in simple_includes:
print(" appending versionfile_source ('%s') to MANIFEST.in" %
cfg.versionfile_source)
with open(manifest_in, "a") as f:
f.write("include %s\n" % cfg.versionfile_source)
else:
print(" versionfile_source already in MANIFEST.in")
# Make VCS-specific changes. For git, this means creating/changing # Make VCS-specific changes. For git, this means creating/changing
# .gitattributes to mark _version.py for export-subst keyword # .gitattributes to mark _version.py for export-subst keyword
# substitution. # substitution.
do_vcs_install(manifest_in, cfg.versionfile_source, ipy) do_vcs_install(cfg.versionfile_source, ipy)
return 0 return 0
...@@ -2094,7 +2160,7 @@ def scan_setup_py(): ...@@ -2094,7 +2160,7 @@ def scan_setup_py():
found = set() found = set()
setters = False setters = False
errors = 0 errors = 0
with open("setup.py") as f: with open("setup.py", "r") as f:
for line in f.readlines(): for line in f.readlines():
if "import versioneer" in line: if "import versioneer" in line:
found.add("import") found.add("import")
...@@ -2126,10 +2192,14 @@ def scan_setup_py(): ...@@ -2126,10 +2192,14 @@ def scan_setup_py():
return errors return errors
def setup_command():
"""Set up Versioneer and exit with appropriate error code."""
errors = do_setup()
errors += scan_setup_py()
sys.exit(1 if errors else 0)
if __name__ == "__main__": if __name__ == "__main__":
cmd = sys.argv[1] cmd = sys.argv[1]
if cmd == "setup": if cmd == "setup":
errors = do_setup() setup_command()
errors += scan_setup_py()
if errors:
sys.exit(1)
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论