mirror of
https://github.com/Stone-Red-Code/dvc.git
synced 2026-11-01 23:08:43 +01:00
use setuptools_scm and build_requires (#6775)
* use setuptools_scm and build_requires * remove kwargs from setup.py * fix tests; fetch in depth in github actions * use build to build sdist/wheel * try updating pip and setuptools * update pip * remove or unpin wheel * bring back the version in pyinstaller * Update scripts/build.py Co-authored-by: Ruslan Kuprieiev <[email protected]>
This commit is contained in:
co-authored by
Ruslan Kuprieiev
parent
53255c22b3
commit
8abda93e3e
@@ -11,6 +11,8 @@ jobs:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/[email protected]
|
||||
with:
|
||||
@@ -40,6 +42,8 @@ jobs:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/[email protected]
|
||||
with:
|
||||
@@ -69,6 +73,8 @@ jobs:
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/[email protected]
|
||||
with:
|
||||
@@ -101,6 +107,8 @@ jobs:
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/[email protected]
|
||||
with:
|
||||
@@ -129,6 +137,8 @@ jobs:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/[email protected]
|
||||
with:
|
||||
@@ -136,7 +146,9 @@ jobs:
|
||||
- name: Install
|
||||
run: pip install ".[all,tests]"
|
||||
- name: Build packages
|
||||
run: ./scripts/build_package.sh
|
||||
run: |
|
||||
pip install -U pip
|
||||
./scripts/build_package.sh
|
||||
- name: Publish packages to PyPI
|
||||
if: github.event_name == 'release'
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
|
||||
@@ -21,6 +21,8 @@ jobs:
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/[email protected]
|
||||
with:
|
||||
@@ -50,6 +52,8 @@ jobs:
|
||||
pyv: "3.10"
|
||||
steps:
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python
|
||||
uses: actions/[email protected]
|
||||
with:
|
||||
|
||||
@@ -38,3 +38,4 @@ scripts/ci/gcp-creds.json
|
||||
.gcp-creds.json
|
||||
|
||||
*~
|
||||
/dvc/_version.py
|
||||
|
||||
@@ -7,6 +7,11 @@ repos:
|
||||
language_version: python3
|
||||
repo: https://github.com/ambv/black
|
||||
rev: 21.9b0
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.0.1
|
||||
hooks:
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
- hooks:
|
||||
- id: codespell
|
||||
args:
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
include fastentrypoints.py
|
||||
include LICENSE
|
||||
|
||||
+6
-74
@@ -1,74 +1,6 @@
|
||||
# Used in setup.py, so don't pull any additional dependencies
|
||||
#
|
||||
# Based on:
|
||||
# - https://github.com/python/mypy/blob/master/mypy/version.py
|
||||
# - https://github.com/python/mypy/blob/master/mypy/git.py
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
_BASE_VERSION = "2.8.1"
|
||||
|
||||
|
||||
def _generate_version(base_version):
|
||||
"""Generate a version with information about the Git repository."""
|
||||
pkg_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
if not _is_git_repo(pkg_dir) or not _have_git():
|
||||
return base_version
|
||||
|
||||
if _is_release(pkg_dir, base_version) and not _is_dirty(pkg_dir):
|
||||
return base_version
|
||||
|
||||
return "{base_version}+{short_sha}{dirty}".format(
|
||||
base_version=base_version,
|
||||
short_sha=_git_revision(pkg_dir).decode("utf-8")[0:6],
|
||||
dirty=".mod" if _is_dirty(pkg_dir) else "",
|
||||
)
|
||||
|
||||
|
||||
def _is_git_repo(dir_path):
|
||||
"""Is the given directory version-controlled with Git?"""
|
||||
return os.path.exists(os.path.join(dir_path, ".git"))
|
||||
|
||||
|
||||
def _have_git():
|
||||
"""Can we run the git executable?"""
|
||||
try:
|
||||
subprocess.check_output(["git", "--help"])
|
||||
return True
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def _is_release(dir_path, base_version):
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
["git", "describe", "--tags", "--exact-match"],
|
||||
cwd=dir_path,
|
||||
stderr=subprocess.STDOUT,
|
||||
).decode("utf-8")
|
||||
tag = output.strip()
|
||||
return tag == base_version
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
|
||||
|
||||
def _git_revision(dir_path):
|
||||
"""Get SHA of the HEAD of a Git repository."""
|
||||
return subprocess.check_output(
|
||||
["git", "rev-parse", "HEAD"], cwd=dir_path
|
||||
).strip()
|
||||
|
||||
|
||||
def _is_dirty(dir_path):
|
||||
"""Check whether a git repository has uncommitted changes."""
|
||||
try:
|
||||
subprocess.check_call(["git", "diff", "--quiet"], cwd=dir_path)
|
||||
return False
|
||||
except subprocess.CalledProcessError:
|
||||
return True
|
||||
|
||||
|
||||
__version__ = _generate_version(_BASE_VERSION)
|
||||
# pylint: disable=unused-import
|
||||
try:
|
||||
from ._version import version as __version__
|
||||
from ._version import version_tuple # noqa: F401
|
||||
except ImportError:
|
||||
__version__ = "UNKNOWN"
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
# noqa: D300,D400
|
||||
# Copyright (c) 2016, Aaron Christianson
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
Monkey patch setuptools to write faster console_scripts with this format:
|
||||
|
||||
import sys
|
||||
from mymodule import entry_function
|
||||
sys.exit(entry_function())
|
||||
|
||||
This is better.
|
||||
|
||||
(c) 2016, Aaron Christianson
|
||||
http://github.com/ninjaaron/fast-entry_points
|
||||
"""
|
||||
import re
|
||||
|
||||
from setuptools.command import easy_install
|
||||
|
||||
TEMPLATE = r"""
|
||||
# -*- coding: utf-8 -*-
|
||||
# EASY-INSTALL-ENTRY-SCRIPT: '{3}','{4}','{5}'
|
||||
__requires__ = '{3}'
|
||||
import re
|
||||
import sys
|
||||
|
||||
from {0} import {1}
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit({2}())
|
||||
""".lstrip()
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_args(cls, dist, header=None): # noqa: D205,D400
|
||||
"""
|
||||
Yield write_script() argument tuples for a distribution's
|
||||
console_scripts and gui_scripts entry points.
|
||||
"""
|
||||
if header is None:
|
||||
# pylint: disable=E1101
|
||||
header = cls.get_header()
|
||||
spec = str(dist.as_requirement())
|
||||
for type_ in "console", "gui":
|
||||
group = type_ + "_scripts"
|
||||
for name, ep in dist.get_entry_map(group).items():
|
||||
# ensure_safe_name
|
||||
if re.search(r"[\\/]", name):
|
||||
raise ValueError("Path separators not allowed in script names")
|
||||
script_text = TEMPLATE.format(
|
||||
ep.module_name,
|
||||
ep.attrs[0],
|
||||
".".join(ep.attrs),
|
||||
spec,
|
||||
group,
|
||||
name,
|
||||
)
|
||||
# pylint: disable=E1101,protected-access
|
||||
args = cls._get_script_args(type_, name, header, script_text)
|
||||
yield from args
|
||||
|
||||
|
||||
# pylint: disable=E1101
|
||||
easy_install.ScriptWriter.get_args = get_args
|
||||
|
||||
|
||||
def main():
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
dests = sys.argv[1:] or ["."]
|
||||
filename = re.sub(r"\.pyc$", ".py", __file__)
|
||||
|
||||
for dst in dests:
|
||||
shutil.copy(filename, dst)
|
||||
manifest_path = os.path.join(dst, "MANIFEST.in")
|
||||
setup_path = os.path.join(dst, "setup.py")
|
||||
|
||||
# Insert the include statement to MANIFEST.in if not present
|
||||
with open(manifest_path, "a+") as manifest:
|
||||
manifest.seek(0)
|
||||
manifest_content = manifest.read()
|
||||
if "include fastentrypoints.py" not in manifest_content:
|
||||
manifest.write(
|
||||
("\n" if manifest_content else "")
|
||||
+ "include fastentrypoints.py"
|
||||
)
|
||||
|
||||
# Insert the import statement to setup.py if not present
|
||||
with open(setup_path, "a+") as setup:
|
||||
setup.seek(0)
|
||||
setup_content = setup.read()
|
||||
if "import fastentrypoints" not in setup_content:
|
||||
setup.seek(0)
|
||||
setup.truncate()
|
||||
setup.write("import fastentrypoints\n" + setup_content)
|
||||
@@ -1,3 +1,17 @@
|
||||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=58.2",
|
||||
"wheel",
|
||||
"setuptools_scm[toml]>=6.3.1",
|
||||
# Prevents pkg_resources import in entry point script,
|
||||
# see https://github.com/ninjaaron/fast-entry_points.
|
||||
# This saves about 200 ms on startup time for non-wheel installs.
|
||||
"fastentrypoints>=0.12"
|
||||
]
|
||||
|
||||
[tool.setuptools_scm]
|
||||
write_to = "dvc/_version.py"
|
||||
|
||||
[tool.black]
|
||||
line-length = 79
|
||||
include = '\.pyi?$'
|
||||
|
||||
@@ -10,5 +10,5 @@ fi
|
||||
|
||||
echo 'PKG = "pip"' >dvc/utils/build.py
|
||||
|
||||
python setup.py sdist
|
||||
python setup.py bdist_wheel
|
||||
python -m pip install -U build setuptools>=58.2
|
||||
python -m build --sdist --wheel --outdir dist/
|
||||
|
||||
@@ -9,5 +9,6 @@ fi
|
||||
|
||||
rm -rf dist/
|
||||
pip install twine
|
||||
python setup.py sdist bdist_wheel
|
||||
python -m pip install -U build setuptools>=58.2
|
||||
python -m build --sdist --wheel --outdir dist/
|
||||
twine upload dist/*
|
||||
|
||||
@@ -26,10 +26,15 @@ classifiers =
|
||||
Programming Language :: Python :: 3.10
|
||||
|
||||
[options]
|
||||
setup_requires =
|
||||
setuptools>=58.2
|
||||
setuptools_scm[toml]>=6.3.1
|
||||
fastentrypoints>=0.12
|
||||
python_requires = >=3.6
|
||||
zip_safe = False
|
||||
packages = find:
|
||||
include_package_data = True
|
||||
use_scm_version = True
|
||||
install_requires =
|
||||
# See https://github.com/pyinstaller/pyinstaller/issues/1945
|
||||
ply>=3.9
|
||||
|
||||
@@ -1,66 +1,3 @@
|
||||
import importlib.util
|
||||
import os
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools.command.build_py import build_py as _build_py
|
||||
|
||||
# Prevents pkg_resources import in entry point script,
|
||||
# see https://github.com/ninjaaron/fast-entry_points.
|
||||
# This saves about 200 ms on startup time for non-wheel installs.
|
||||
try:
|
||||
import fastentrypoints # noqa: F401, pylint: disable=unused-import
|
||||
except ImportError:
|
||||
pass # not able to import when installing through pre-commit
|
||||
|
||||
|
||||
# Read package meta-data from version.py
|
||||
# see https://packaging.python.org/guides/single-sourcing-package-version/
|
||||
try:
|
||||
pkg_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
version_path = os.path.join(pkg_dir, "dvc", "version.py")
|
||||
spec = importlib.util.spec_from_file_location("dvc.version", version_path)
|
||||
dvc_version = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(dvc_version)
|
||||
version = dvc_version.__version__ # noqa: F821
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
# Dependabot seem to stop working when we don't handle this except block.
|
||||
# Most likely, it's because of the restrictions in execution of the module.
|
||||
# We workaround it, and print the error message if it also happens on other
|
||||
# installations (though this message may likely be suppressed by build
|
||||
# tools, eg: pip only shows this message on `--verbose` mode).
|
||||
import sys
|
||||
|
||||
print("Could not load version info: ", exc, file=sys.stderr)
|
||||
version = "UNKNOWN"
|
||||
|
||||
|
||||
# To achieve consistency between the build version and the one provided
|
||||
# by your package during runtime, you need to **pin** the build version.
|
||||
#
|
||||
# This custom class will replace the version.py module with a **static**
|
||||
# `__version__` that your package can read at runtime, assuring consistency.
|
||||
#
|
||||
# References:
|
||||
# - https://docs.python.org/3.7/distutils/extending.html
|
||||
# - https://github.com/python/mypy
|
||||
class build_py(_build_py):
|
||||
def pin_version(self):
|
||||
ver_file = Path(self.build_lib) / "dvc" / "version.py"
|
||||
self.mkpath(os.fspath(ver_file.parent))
|
||||
ver_file.write_text(
|
||||
dedent(
|
||||
f"""\
|
||||
# AUTOGENERATED at build time by setup.py
|
||||
__version__ = "{version}"
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.execute(self.pin_version, ())
|
||||
_build_py.run(self)
|
||||
|
||||
|
||||
setup(version=version, cmdclass={"build_py": build_py})
|
||||
setup()
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import re
|
||||
|
||||
from dvc.main import main
|
||||
from tests.unit.test_info import PYTHON_VERSION_REGEX, find_supported_remotes
|
||||
from tests.unit.test_info import (
|
||||
DVC_VERSION_REGEX,
|
||||
PYTHON_VERSION_REGEX,
|
||||
find_supported_remotes,
|
||||
)
|
||||
|
||||
|
||||
def test_(tmp_dir, dvc, scm, capsys):
|
||||
assert main(["version"]) == 0
|
||||
|
||||
out, _ = capsys.readouterr()
|
||||
assert re.search(r"DVC version: \d+\.\d+\.\d+.*", out)
|
||||
assert re.search(fr"DVC version: {DVC_VERSION_REGEX}", out)
|
||||
assert re.search(f"Platform: {PYTHON_VERSION_REGEX} on .*", out)
|
||||
assert find_supported_remotes(out)
|
||||
assert re.search(r"Cache types: .*", out)
|
||||
|
||||
@@ -11,6 +11,7 @@ from dvc.info import get_dvc_info
|
||||
# `patch` is more than enough for the tests.
|
||||
# Refer PEP-0440 for complete regex just in-case.
|
||||
PYTHON_VERSION_REGEX = r"Python \d\.\d+\.\d+\S*"
|
||||
DVC_VERSION_REGEX = r"\d+\.\d+\.(\d+\.)?.*"
|
||||
|
||||
|
||||
def find_supported_remotes(string):
|
||||
@@ -50,7 +51,7 @@ def test_info_in_repo(scm_init, tmp_dir):
|
||||
|
||||
dvc_info = get_dvc_info()
|
||||
|
||||
assert re.search(r"DVC version: \d+\.\d+\.\d+.*", dvc_info)
|
||||
assert re.search(fr"DVC version: {DVC_VERSION_REGEX}", dvc_info)
|
||||
assert re.search(f"Platform: {PYTHON_VERSION_REGEX} on .*", dvc_info)
|
||||
assert find_supported_remotes(dvc_info)
|
||||
assert re.search(r"Cache types: .*", dvc_info)
|
||||
@@ -125,7 +126,7 @@ def test_fs_info_in_repo(tmp_dir, dvc, caplog):
|
||||
def test_info_outside_of_repo(tmp_dir, caplog):
|
||||
dvc_info = get_dvc_info()
|
||||
|
||||
assert re.search(r"DVC version: \d+\.\d+\.\d+.*", dvc_info)
|
||||
assert re.search(fr"DVC version: {DVC_VERSION_REGEX}", dvc_info)
|
||||
assert re.search(f"Platform: {PYTHON_VERSION_REGEX} on .*", dvc_info)
|
||||
assert find_supported_remotes(dvc_info)
|
||||
assert not re.search(r"Cache types: .*", dvc_info)
|
||||
@@ -134,7 +135,7 @@ def test_info_outside_of_repo(tmp_dir, caplog):
|
||||
|
||||
def test_fs_info_outside_of_repo(tmp_dir, caplog):
|
||||
dvc_info = get_dvc_info()
|
||||
assert re.search(r"DVC version: \d+\.\d+\.\d+.*", dvc_info)
|
||||
assert re.search(fr"DVC version: {DVC_VERSION_REGEX}", dvc_info)
|
||||
assert re.search(f"Platform: {PYTHON_VERSION_REGEX} on .*", dvc_info)
|
||||
assert find_supported_remotes(dvc_info)
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import subprocess
|
||||
|
||||
import dvc.version
|
||||
|
||||
|
||||
def test_is_release(mocker):
|
||||
m = mocker.patch.object(dvc.version.subprocess, "check_output")
|
||||
|
||||
m.side_effect = subprocess.CalledProcessError(1, "cmd")
|
||||
ret = dvc.version._is_release(None, dvc.version._BASE_VERSION)
|
||||
assert ret is False
|
||||
|
||||
m.side_effect = None
|
||||
m.return_value = dvc.version._BASE_VERSION.encode("ascii")
|
||||
ret = dvc.version._is_release(None, dvc.version._BASE_VERSION)
|
||||
assert ret
|
||||
|
||||
m.return_value = b"630d1741c2d5dd89a3176bd15b63121b905d35c9"
|
||||
ret = dvc.version._is_release(None, dvc.version._BASE_VERSION)
|
||||
assert ret is False
|
||||
Reference in New Issue
Block a user