Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
update: true
- run: pip install -U setuptools
if: matrix.python_version != 'msys2'
- run: pip install -e .[toml] pytest
- run: pip install -e .[toml] pytest virtualenv
# pip2 is needed because Mercurial uses python2 on Ubuntu 20.04
- run: |
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
Expand All @@ -49,6 +49,17 @@ jobs:
if: matrix.os == 'ubuntu-latest'
- run: pytest

test_legacy_setuptools:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Setup python
uses: actions/setup-python@v2
with:
python-version: "3.6"
architecture: x64
- run: pip install -e .[toml] pytest virtualenv
- run: pytest --test-legacy testing/test_setuptools_support.py || true # ignore fail flaky on ci
check_selfinstall:
runs-on: ubuntu-latest
strategy:
Expand All @@ -66,8 +77,8 @@ jobs:
architecture: x64
# self install testing needs some clarity
# so its being executed without any other tools running
# setuptools smaller 52 is needed too di easy_install
- run: pip install -U "setuptools<52" tomli
# setuptools smaller 52 is needed to do easy_install
- run: pip install -U "setuptools<52" tomli packaging
- run: python setup.py egg_info
- run: python setup.py sdist
- run: ${{ matrix.installer }} dist/*
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: 21.7b0
rev: 21.8b0
hooks:
- id: black
args: [--safe, --quiet]
Expand All @@ -21,7 +21,7 @@ repos:
hooks:
- id: flake8
- repo: https://github.com/asottile/pyupgrade
rev: v2.24.0
rev: v2.25.0
hooks:
- id: pyupgrade
args: [--py36-plus]
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@

6.3.0
=======

.. warning::

This release explicitly warns on unsupported setuptools.
This unfortunately has to happen as the legacy ``setup_requires`` mechanism
incorrectly configures the setuptools working-set when a more recent setuptools
version than available is required.

As all releases of setuptools are affected as the historic mechanism
for ensuring a working setuptools setup was shipping a ``ez_setup`` file
next to ``setup.py``, which would install the required version of setuptools.

This mechanism has long since been deprecated and removed
as most people haven't been using it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
as most people haven't been using it
as most people haven't been using it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, on this block, mentioning what version is too old and going to produce the warning might be helpful. This starts by saying "warns on legacy" but then says "all releases are affected", it's really unclear, I think. Maybe I could suggest a new version?

This release explicitly warns on unsupported setuptools. When using the legacy 
``setup_requires`` mechanism, you cannot request a newer setuptools than the
one currently running (this was the reason PEP 517 and PEP 518 were written).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all setuptools releases are affected by the issue, even those that dont trigger legacy problems

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but this is a general issue that has nothing to do with setuptools_scm, it's just easy to hit with it. The current message makes it sound like it's a major issue that most users will hit, while in actuality, now that 31+ is supported instead of 45+, it's only going to pop up in very old systems - which you should be unlikely to hit with Python 3.6+ (though possible). My suggested replacement makes it sound a little less drastic and pushes people gently toward PEP 517/518, which avoids triggering legacy behavior.



* fix #612: depend on packaging to ensure version parsing parts
* fix #611: correct the typo that hid away the toml extra and add it in ``setup.py`` as well
* fix #615: restore support for the git_archive plugin which doesn't pass over the config
* restore the ability to run on old setuptools while to avoid breaking pipelines

v6.2.0
=======

Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ include *.rst
include LICENSE
include *.toml
include mypy.ini
include testing/Dockerfile.busted-buster
recursive-include testing *.bash
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
[build-system]
requires = ["setuptools>=45", "wheel", "tomli"]
requires = [
"setuptools>=45",
"wheel",
"tomli>=1.0",
"packaging>=20.0"
]
build-backend = "setuptools.build_meta"
10 changes: 6 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ classifiers =
[options]
packages = find:
install_requires =
setuptools>=45
tomli>=1.0
packaging>=20.0
setuptools
python_requires = >=3.6
package_dir =
=src
Expand Down Expand Up @@ -68,5 +68,7 @@ setuptools_scm.version_scheme =
no-guess-dev = setuptools_scm.version:no_guess_dev_version
calver-by-date = setuptools_scm.version:calver_by_date

[option.extras_require]
toml = # empty
[options.extras_require]
toml =
setuptools>=42
tomli>=1.0.0
60 changes: 31 additions & 29 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,54 +13,56 @@
import sys

import setuptools
from setuptools.command.bdist_egg import bdist_egg as original_bdist_egg


def scm_config():
class bdist_egg(original_bdist_egg):
def run(self):
raise SystemExit(
f"{type(self).__name__} is forbidden, "
"please update to setuptools>=45 which uses pip"
)


def scm_version():

if sys.version_info < (3, 6):
raise RuntimeError(
"support for python < 3.6 has been removed in setuptools_scm>=6.0.0"
)

here = os.path.dirname(os.path.abspath(__file__))
src = os.path.join(here, "src")
egg_info = os.path.join(src, "setuptools_scm.egg-info")
has_entrypoints = os.path.isdir(egg_info)
import pkg_resources

sys.path.insert(0, src)
pkg_resources.working_set.add_entry(src)

from setuptools_scm import get_version
from setuptools_scm.hacks import parse_pkginfo
from setuptools_scm.git import parse as parse_git
from setuptools_scm import git
from setuptools_scm.version import guess_next_dev_version, get_local_node_and_date

def parse(root):
def parse(root, config):
try:
return parse_pkginfo(root)
return parse_pkginfo(root, config)
except OSError:
return parse_git(root)
return git.parse(root, config=config)

config = dict(
version_scheme=guess_next_dev_version, local_scheme=get_local_node_and_date
return get_version(
root=here,
parse=parse,
version_scheme=guess_next_dev_version,
local_scheme=get_local_node_and_date,
)

from setuptools.command.bdist_egg import bdist_egg as original_bdist_egg

class bdist_egg(original_bdist_egg):
def run(self):
raise SystemExit("bdist_egg is forbidden, please update to setuptools>=45")

if has_entrypoints:
return dict(use_scm_version=config, cmdclass={"bdist_egg": bdist_egg})
else:
from setuptools_scm import get_version

return dict(
version=get_version(root=here, parse=parse, **config),
cmdclass={"bdist_egg": bdist_egg},
)


if __name__ == "__main__":
setuptools.setup(setup_requires=["setuptools>=45", "tomli"], **scm_config())
setuptools.setup(
setup_requires=["setuptools"],
version=scm_version(),
extras_require={
"toml": [
"setuptools>=42",
"tomli>=1.0.0",
],
},
cmdclass={"bdist_egg": bdist_egg},
)
44 changes: 21 additions & 23 deletions src/setuptools_scm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,17 @@
import os
import warnings

try:
from packaging.version import parse
except ImportError:
from pkg_resources import parse_version as parse

from .config import (
Configuration,
DEFAULT_VERSION_SCHEME,
DEFAULT_LOCAL_SCHEME,
DEFAULT_TAG_REGEX,
NonNormalizedVersion,
)
from .utils import function_has_arg, trace
from .version import format_version, meta
from ._version_cls import NonNormalizedVersion
from ._version_cls import Version
from .config import Configuration
from .config import DEFAULT_LOCAL_SCHEME
from .config import DEFAULT_TAG_REGEX
from .config import DEFAULT_VERSION_SCHEME
from .discover import iter_matching_entrypoints
from .utils import function_has_arg
from .utils import trace
from .version import format_version
from .version import meta

PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION"
PRETEND_KEY_NAMED = PRETEND_KEY + "_FOR_{name}"
Expand All @@ -40,9 +36,9 @@ def version_from_scm(root):
warnings.warn(
"version_from_scm is deprecated please use get_version",
category=DeprecationWarning,
stacklevel=2,
)
config = Configuration()
config.root = root
config = Configuration(root=root)
# TODO: Is it API?
return _version_from_entrypoints(config)

Expand All @@ -52,15 +48,16 @@ def _call_entrypoint_fn(root, config, fn):
return fn(root, config=config)
else:
warnings.warn(
"parse functions are required to provide a named argument"
" 'config' in the future.",
category=PendingDeprecationWarning,
f"parse function {fn.__module__}.{fn.__name__}"
" are required to provide a named argument"
" 'config', setuptools_scm>=8.0 will remove support.",
category=DeprecationWarning,
stacklevel=2,
)
return fn(root)


def _version_from_entrypoints(config, fallback=False):
def _version_from_entrypoints(config: Configuration, fallback=False):
if fallback:
entrypoint = "setuptools_scm.parse_scm_fallback"
root = config.fallback_root
Expand All @@ -70,7 +67,7 @@ def _version_from_entrypoints(config, fallback=False):

for ep in iter_matching_entrypoints(root, entrypoint, config):
version = _call_entrypoint_fn(root, config, ep.load())

trace(ep, version)
if version:
return version

Expand All @@ -90,7 +87,7 @@ def dump_version(root, version, write_to, template=None):
)
)

parsed_version = parse(version)
parsed_version = Version(version)
version_fields = parsed_version.release
if parsed_version.dev is not None:
version_fields += (f"dev{parsed_version.dev}",)
Expand Down Expand Up @@ -201,10 +198,11 @@ def _get_version(config):
"dump_version",
"version_from_scm",
"Configuration",
"NonNormalizedVersion",
"DEFAULT_VERSION_SCHEME",
"DEFAULT_LOCAL_SCHEME",
"DEFAULT_TAG_REGEX",
"Version",
"NonNormalizedVersion",
# TODO: are the symbols below part of public API ?
"function_has_arg",
"trace",
Expand Down
49 changes: 49 additions & 0 deletions src/setuptools_scm/_version_cls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
try:
from packaging.version import Version

assert hasattr(Version, "release")
except ImportError:
from pkg_resources._vendor.packaging.version import Version as SetuptoolsVersion

try:
SetuptoolsVersion.release
Version = SetuptoolsVersion
except AttributeError:

class Version(SetuptoolsVersion): # type: ignore
@property
def release(self):
return self._version.release

@property
def dev(self):
return self._version.dev

@property
def local(self):
return self._version.local


class NonNormalizedVersion(Version):
"""A non-normalizing version handler.

You can use this class to preserve version verification but skip normalization.
For example you can use this to avoid git release candidate version tags
("1.0.0-rc1") to be normalized to "1.0.0rc1". Only use this if you fully
trust the version tags.
"""

def __init__(self, version):
# parse and validate using parent
super().__init__(version)

# store raw for str
self._raw_version = version

def __str__(self):
# return the non-normalized version (parent returns the normalized)
return self._raw_version

def __repr__(self):
# same pattern as parent
return f"<NonNormalizedVersion({self._raw_version!r})>"
Loading