Skip to content

Commit

Permalink
Stopped using pkg_resources, as it's deprecated.
Browse files Browse the repository at this point in the history
  • Loading branch information
facundobatista committed May 25, 2024
1 parent 08c1ee3 commit 2160d59
Show file tree
Hide file tree
Showing 16 changed files with 137 additions and 112 deletions.
15 changes: 1 addition & 14 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -596,20 +596,7 @@ Else, keep reading to know how to install the dependencies first, and
Dependencies
------------

Besides needing Python 3.3 or greater, fades depends also on the
``pkg_resources`` package, that comes in with ``setuptools``.
It's installed almost everywhere, but in any case,
you can install it in Ubuntu/Debian with::

apt-get install python3-setuptools

And on Arch Linux with::

pacman -S python-setuptools

It also depends on ``python-xdg`` package. This package should be
installed on any GNU/Linux OS wiht a freedesktop.org GUI. However it
is an **optional** dependency.
Besides needing Python 3.3 or greater, fades depends on the ``python-xdg`` package. This package should be installed on any GNU/Linux OS wiht a freedesktop.org GUI. However it is an **optional** dependency.

You can install it in Ubuntu/Debian with::

Expand Down
36 changes: 20 additions & 16 deletions fades/cache.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2015-2016 Facundo Batista, Nicolás Demarchi
# Copyright 2015-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General
Expand All @@ -23,11 +23,8 @@
import time

from fades import REPO_VCS

from pkg_resources import Distribution

from fades.multiplatform import filelock
from fades.parsing import VCSDependency
from fades.parsing import VCSDependency, NameVerDependency

logger = logging.getLogger(__name__)

Expand All @@ -44,9 +41,10 @@ def __init__(self, filepath):
def _venv_match(self, installed, requirements):
"""Return True if what is installed satisfies the requirements.
This method has multiple exit-points, but only for False (because
This method has multiple exit-points, but only for None (because
if *anything* is not satisified, the venv is no good). Only after
all was checked, and it didn't exit, the venv is ok so return True.
all was checked, and it didn't exit, the venv is ok and it so
returns the satisfying dependencies.
"""
if not requirements:
# special case for no requirements, where we can't actually
Expand All @@ -61,22 +59,28 @@ def _venv_match(self, installed, requirements):
return None

if repo == REPO_VCS:
inst_deps = {VCSDependency(url) for url in installed[repo].keys()}
inst_namevers = {(url, None) for url in installed[repo].keys()}
else:
inst_deps = {Distribution(project_name=dep, version=ver)
for (dep, ver) in installed[repo].items()}
inst_namevers = {(dep, ver) for (dep, ver) in installed[repo].items()}

for req in req_deps:
for inst in inst_deps:
if inst in req:
useful_inst.add(inst)
for inst_name, inst_ver in inst_namevers:
if req.name == inst_name and req.specifier.contains(inst_ver):
useful_inst.add((inst_name, inst_ver))
break
else:
# nothing installed satisfied that requirement
return None

# assure *all* that is installed is useful for the requirements
if useful_inst == inst_deps:
satisfying_deps.extend(inst_deps)
if useful_inst == inst_namevers:
inst_reqs = set()
for name, ver in inst_namevers:
if ver is None:
inst_reqs.add(VCSDependency(name))
else:
inst_reqs.add(NameVerDependency(name, ver))
satisfying_deps.extend(inst_reqs)
else:
return None

Expand Down Expand Up @@ -106,7 +110,7 @@ def _select_better_fit(self, matching_venvs):
# position of the winner
scores = [0] * len(venvs)
for dependencies in zip(*to_compare):
if not isinstance(dependencies[0], Distribution):
if not isinstance(dependencies[0], NameVerDependency):
# only distribution URLs can be compared
continue

Expand Down
4 changes: 2 additions & 2 deletions fades/envbuilder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2014-2016 Facundo Batista, Nicolás Demarchi
# Copyright 2014-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
Expand Down Expand Up @@ -169,7 +169,7 @@ def create_venv(requested_deps, interpreter, is_current, options, pip_options, a
# always store the installed dependency, as in the future we'll select the venv
# based on what is installed, not what used requested (remember that user may
# request >, >=, etc!)
project = dependency.project_name
project = dependency.name
version = mgr.get_version(project)
installed[repo][project] = version

Expand Down
37 changes: 19 additions & 18 deletions fades/helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2014-2015 Facundo Batista, Nicolás Demarchi
# Copyright 2014-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
Expand All @@ -22,11 +22,11 @@
import logging
import subprocess
import tempfile

from urllib import request, parse
from urllib.error import HTTPError

import pkg_resources
from packaging.requirements import Requirement
from packaging.version import Version

from fades import FadesError, HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK, _version

Expand Down Expand Up @@ -183,44 +183,45 @@ def check_pypi_updates(dependencies):
for dependency in dependencies.get('pypi', []):
# get latest version from PyPI api
try:
latest_version = get_latest_version_number(dependency.project_name)
latest_version = Version(get_latest_version_number(dependency.name))
except Exception as error:
logger.warning("--check-updates command will be aborted. Error: %s", error)
return dependencies
# get required version
required_version = None
if dependency.specs:
_, required_version = dependency.specs[0]

if required_version:
if dependency.specifier:
spec = list(dependency.specifier)[0]
required_version = Version(spec.version)

dependencies_up_to_date.append(dependency)
if latest_version > required_version:
logger.info("There is a new version of %s: %s",
dependency.project_name, latest_version)
dependency.name, latest_version)
elif latest_version < required_version:
logger.warning("The requested version for %s is greater "
"than latest found in PyPI: %s",
dependency.project_name, latest_version)
dependency.name, latest_version)
else:
logger.info("The requested version for %s is the latest one in PyPI: %s",
dependency.project_name, latest_version)
dependency.name, latest_version)
else:
project_name_plus = "{}=={}".format(dependency.project_name, latest_version)
dependencies_up_to_date.append(pkg_resources.Requirement.parse(project_name_plus))
name_plus = "{}=={}".format(dependency.name, latest_version)
dependencies_up_to_date.append(Requirement(name_plus))
logger.info("The latest version of %r is %s and will use it.",
dependency.project_name, latest_version)
dependency.name, latest_version)

dependencies["pypi"] = dependencies_up_to_date
return dependencies


def _pypi_head_package(dependency):
"""Hit pypi with a http HEAD to check if pkg_name exists."""
if dependency.specs:
_, version = dependency.specs[0]
url = BASE_PYPI_URL_WITH_VERSION.format(name=dependency.project_name, version=version)
if dependency.specifier:
spec = list(dependency.specifier)[0]
version = spec.version
url = BASE_PYPI_URL_WITH_VERSION.format(name=dependency.name, version=version)
else:
url = BASE_PYPI_URL.format(name=dependency.project_name)
url = BASE_PYPI_URL.format(name=dependency.name)
logger.debug("Doing HEAD requests against %s", url)
req = request.Request(url, method='HEAD')
try:
Expand Down
46 changes: 36 additions & 10 deletions fades/parsing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2014-2019 Facundo Batista, Nicolás Demarchi
# Copyright 2014-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
Expand All @@ -20,14 +20,23 @@
import os
import re

from pkg_resources import parse_requirements
from packaging.requirements import Requirement
from packaging.version import Version

from fades import REPO_PYPI, REPO_VCS
from fades.pkgnamesdb import MODULE_TO_PACKAGE

logger = logging.getLogger(__name__)


class _VCSSpecifier:
"""A simple specifier that works with VCSDependency."""

def contains(self, other):
"""VCS dependency does not handle versions."""
return other is None


class VCSDependency:
"""A dependency object for VCS urls (git, bzr, etc.).
Expand All @@ -39,7 +48,8 @@ class VCSDependency:

def __init__(self, url):
"""Init."""
self.url = url
self.url = self.name = self.project_name = self.version = url
self.specifier = _VCSSpecifier()

def __str__(self):
"""Return the URL as this is the interface to get what pip will use."""
Expand All @@ -49,10 +59,6 @@ def __repr__(self):
"""Repr."""
return "<VCSDependency: {!r}>".format(self.url)

def __contains__(self, other):
"""Tell if requirement is satisfied."""
return other.url == self.url

def __eq__(self, other):
"""Tell if one VCSDependency is equal to other."""
if not isinstance(other, VCSDependency):
Expand All @@ -64,6 +70,24 @@ def __hash__(self):
return hash(self.url)


class NameVerDependency:
"""A dependency indicated by name and version."""

def __init__(self, name, version):
self.name = name
self.version = Version(version)

def __eq__(self, other):
return self.name == other.name and self.version == other.version

def __hash__(self):
return hash((self.name, self.version))

def __lt__(self, other):
assert not isinstance(self.version, str)
return (self.name, self.version) < (other.name, other.version)


def parse_fade_requirement(text):
"""Return a requirement and repo from the given text, already parsed and converted."""
text = text.strip()
Expand All @@ -85,7 +109,7 @@ def parse_fade_requirement(text):
if repo == REPO_VCS:
dependency = VCSDependency(requirement)
else:
dependency = list(parse_requirements(requirement))[0]
dependency = Requirement(requirement)
return repo, dependency


Expand Down Expand Up @@ -147,7 +171,7 @@ def _parse_content(fh):
else:
package = module

# To match the "safe" name that pkg_resources creates:
# To match the "safe" name
package = package.replace('_', '-')

# get the fades info after 'fades' mark, if any
Expand Down Expand Up @@ -218,7 +242,9 @@ def _parse_requirement(iterable):
deps = {}
for line in iterable:
line = line.strip()
if not line or line[0] == '#':
if "#" in line:
line = line[:line.index("#")]
if not line:
continue

parsed_req = parse_fade_requirement(line)
Expand Down
2 changes: 1 addition & 1 deletion pkg/debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Priority: extra
Build-Depends: debhelper (>= 9),
dh-python,
dh-translations | dh-python,
python3-setuptools,
python3-packaging,
python3-all (>= 3.4),
python3-xdg
Maintainer: Facundo Batista <[email protected]>
Expand Down
3 changes: 1 addition & 2 deletions pkg/snap/snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ parts:
- python3.8-minimal
- python3-distutils
- python3-minimal
- python3-pkg-resources
- python3-pip
- python3-setuptools
- python3-packaging
- python3-venv
- python3-wheel

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
flake8
logassert
packaging
pydocstyle
pytest
pyuca
Expand Down
8 changes: 3 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3

# Copyright 2014-2017 Facundo Batista, Nicolás Demarchi
# Copyright 2014-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
Expand All @@ -22,8 +22,6 @@
python3
python3-xdg (optional)
python3-pkg-resources
python3-setuptools
"""

import os
Expand Down Expand Up @@ -53,7 +51,7 @@
def get_version():
"""Retrieves package version from the file."""
with open('fades/_version.py') as fh:
m = re.search("\(([^']*)\)", fh.read())
m = re.search(r"\(([^']*)\)", fh.read())
if m is None:
raise ValueError("Unrecognized version in 'fades/_version.py'")
return m.groups()[0].replace(', ', '.')
Expand Down Expand Up @@ -127,7 +125,7 @@ def finalize_options(self):
extras_require={
'pyxdg': 'pyxdg',
'virtualenv': 'virtualenv',
'setuptools': 'setuptools',
'packaging': 'packaging',
},

# https://pypi.python.org/pypi?%3Aaction=list_classifiers
Expand Down
8 changes: 4 additions & 4 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2017-2019 Facundo Batista, Nicolás Demarchi
# Copyright 2017-2024 Facundo Batista, Nicolás Demarchi
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
Expand All @@ -19,7 +19,7 @@
import os
from tempfile import mkstemp

from pkg_resources import parse_requirements
from packaging.requirements import Requirement


def get_tempfile(testcase):
Expand Down Expand Up @@ -59,5 +59,5 @@ def get_python_filepaths(roots):


def get_reqs(*items):
"""Transform text requirements into pkg_resources objects."""
return list(parse_requirements(items))
"""Transform text requirements into Requirement objects."""
return [Requirement(item) for item in items]
Loading

0 comments on commit 2160d59

Please sign in to comment.