Skip to content

Commit

Permalink
Drop dependency on Setuptools (#4443)
Browse files Browse the repository at this point in the history
Instead of relying on `setuptools` to get functionality from
`pkg_resources` (which is slowly being refactored away), leverage
`packaging` for the functionality used by Conda-Build.

Co-authored-by: Jannis Leidel <jannis@leidel.info>
  • Loading branch information
jakirkham and jezdez authored Mar 13, 2023
1 parent 5298354 commit bd49d73
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 6 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ repos:
- id: insert-license
files: \.py$
args: [--license-filepath, .github/disclaimer.txt, --no-extra-eol]
exclude: ^conda_build/version.py
2 changes: 1 addition & 1 deletion conda_build/skeletons/cpan.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import codecs
import hashlib
from pkg_resources import parse_version
from glob import glob
import gzip
import json
Expand All @@ -28,6 +27,7 @@
from conda_build.config import get_or_merge_config
from conda_build.utils import on_win, check_call_env
from conda_build.variants import get_default_variant
from conda_build.version import _parse as parse_version

import requests
from conda_build import environ
Expand Down
2 changes: 1 addition & 1 deletion conda_build/skeletons/pypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from os import makedirs, listdir, getcwd, chdir
from os.path import join, isdir, exists, isfile, abspath

from pkg_resources import parse_version
from conda_build.version import _parse as parse_version
import re
from shutil import copy2
import subprocess
Expand Down
2 changes: 1 addition & 1 deletion conda_build/variants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
from functools import lru_cache
from itertools import product
import os.path
from pkg_resources import parse_version
import re
import sys

import yaml

from conda_build.conda_interface import subdir
from conda_build.conda_interface import cc_conda_build
from conda_build.version import _parse as parse_version
from conda_build.utils import ensure_list, get_logger, islist, on_win, trim_empty_keys

DEFAULT_VARIANTS = {
Expand Down
160 changes: 160 additions & 0 deletions conda_build/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Copyright (C) Donald Stufft and individual contributors
# SPDX-License-Identifier: BSD-2-Clause
"""
This file was partially copied from the packaging.version module before the
LegacyVersion class was removed to continue to support version parsing in
a backward-compatible way where PEP 440 support can't be used.
Copyright (c) Donald Stufft and individual contributors.
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.
"""
import re
from typing import Iterator, List, Tuple, Union
from packaging.version import _BaseVersion, Version, InvalidVersion

LegacyCmpKey = Tuple[int, Tuple[str, ...]]


def _parse(version: str) -> Union["_LegacyVersion", "Version"]:
"""
Parse the given version string and return either a :class:`Version` object
or a :class:`_LegacyVersion` object depending on if the given version is
a valid PEP 440 version or a legacy version.
"""
try:
return Version(version)
except InvalidVersion:
return _LegacyVersion(version)


class _LegacyVersion(_BaseVersion):

def __init__(self, version: str) -> None:
self._version = str(version)
self._key = _legacy_cmpkey(self._version)

def __str__(self) -> str:
return self._version

def __repr__(self) -> str:
return f"<_LegacyVersion('{self}')>"

@property
def public(self) -> str:
return self._version

@property
def base_version(self) -> str:
return self._version

@property
def epoch(self) -> int:
return -1

@property
def release(self) -> None:
return None

@property
def pre(self) -> None:
return None

@property
def post(self) -> None:
return None

@property
def dev(self) -> None:
return None

@property
def local(self) -> None:
return None

@property
def is_prerelease(self) -> bool:
return False

@property
def is_postrelease(self) -> bool:
return False

@property
def is_devrelease(self) -> bool:
return False


_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE)

_legacy_version_replacement_map = {
"pre": "c",
"preview": "c",
"-": "final-",
"rc": "c",
"dev": "@",
}


def _parse_version_parts(s: str) -> Iterator[str]:
for part in _legacy_version_component_re.split(s):
part = _legacy_version_replacement_map.get(part, part)

if not part or part == ".":
continue

if part[:1] in "0123456789":
# pad for numeric comparison
yield part.zfill(8)
else:
yield "*" + part

# ensure that alpha/beta/candidate are before final
yield "*final"


def _legacy_cmpkey(version: str) -> LegacyCmpKey:

# We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch
# greater than or equal to 0. This will effectively put the LegacyVersion,
# which uses the defacto standard originally implemented by setuptools,
# as before all PEP 440 versions.
epoch = -1

# This scheme is taken from pkg_resources.parse_version setuptools prior to
# it's adoption of the packaging library.
parts: List[str] = []
for part in _parse_version_parts(version.lower()):
if part.startswith("*"):
# remove "-" before a prerelease tag
if part < "*final":
while parts and parts[-1] == "*final-":
parts.pop()

# remove trailing zeros from each series of numeric parts
while parts and parts[-1] == "00000000":
parts.pop()

parts.append(part)

return epoch, tuple(parts)
2 changes: 1 addition & 1 deletion recipe/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ requirements:
- filelock
- futures # [py<3]
- jinja2
- packaging
- patchelf # [linux]
- patch >=2.6 # [not win]
- m2-patch >=2.6 # [win]
Expand All @@ -44,7 +45,6 @@ requirements:
- pyyaml
- requests
- scandir # [py<34]
- setuptools
- six
- glob2 >=0.6
- pytz
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"psutil",
"six",
"libarchive-c",
"setuptools",
"packaging",
# "conda-package-handling", # remove comment once released on PyPI
"glob2",
]
Expand Down
2 changes: 1 addition & 1 deletion tests/test_api_skeleton.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import subprocess
import sys

from pkg_resources import parse_version
from conda_build.version import _parse as parse_version
import pytest
import ruamel.yaml

Expand Down

0 comments on commit bd49d73

Please sign in to comment.