Przeglądaj źródła

Drop deprecated pkg_resources in favor of importlib.metadata and packaging.

Dan Helfman 2 lat temu
rodzic
commit
8cb5a42a9e

+ 13 - 13
borgmatic/borg/feature.py

@@ -1,6 +1,6 @@
 from enum import Enum
 
-from pkg_resources import parse_version
+from packaging.version import parse
 
 
 class Feature(Enum):
@@ -18,17 +18,17 @@ class Feature(Enum):
 
 
 FEATURE_TO_MINIMUM_BORG_VERSION = {
-    Feature.COMPACT: parse_version('1.2.0a2'),  # borg compact
-    Feature.ATIME: parse_version('1.2.0a7'),  # borg create --atime
-    Feature.NOFLAGS: parse_version('1.2.0a8'),  # borg create --noflags
-    Feature.NUMERIC_IDS: parse_version('1.2.0b3'),  # borg create/extract/mount --numeric-ids
-    Feature.UPLOAD_RATELIMIT: parse_version('1.2.0b3'),  # borg create --upload-ratelimit
-    Feature.SEPARATE_REPOSITORY_ARCHIVE: parse_version('2.0.0a2'),  # --repo with separate archive
-    Feature.RCREATE: parse_version('2.0.0a2'),  # borg rcreate
-    Feature.RLIST: parse_version('2.0.0a2'),  # borg rlist
-    Feature.RINFO: parse_version('2.0.0a2'),  # borg rinfo
-    Feature.MATCH_ARCHIVES: parse_version('2.0.0b3'),  # borg --match-archives
-    Feature.EXCLUDED_FILES_MINUS: parse_version('2.0.0b5'),  # --list --filter uses "-" for excludes
+    Feature.COMPACT: parse('1.2.0a2'),  # borg compact
+    Feature.ATIME: parse('1.2.0a7'),  # borg create --atime
+    Feature.NOFLAGS: parse('1.2.0a8'),  # borg create --noflags
+    Feature.NUMERIC_IDS: parse('1.2.0b3'),  # borg create/extract/mount --numeric-ids
+    Feature.UPLOAD_RATELIMIT: parse('1.2.0b3'),  # borg create --upload-ratelimit
+    Feature.SEPARATE_REPOSITORY_ARCHIVE: parse('2.0.0a2'),  # --repo with separate archive
+    Feature.RCREATE: parse('2.0.0a2'),  # borg rcreate
+    Feature.RLIST: parse('2.0.0a2'),  # borg rlist
+    Feature.RINFO: parse('2.0.0a2'),  # borg rinfo
+    Feature.MATCH_ARCHIVES: parse('2.0.0b3'),  # borg --match-archives
+    Feature.EXCLUDED_FILES_MINUS: parse('2.0.0b5'),  # --list --filter uses "-" for excludes
 }
 
 
@@ -37,4 +37,4 @@ def available(feature, borg_version):
     Given a Borg Feature constant and a Borg version string, return whether that feature is
     available in that version of Borg.
     '''
-    return FEATURE_TO_MINIMUM_BORG_VERSION[feature] <= parse_version(borg_version)
+    return FEATURE_TO_MINIMUM_BORG_VERSION[feature] <= parse(borg_version)

+ 6 - 2
borgmatic/commands/borgmatic.py

@@ -8,7 +8,11 @@ from queue import Queue
 from subprocess import CalledProcessError
 
 import colorama
-import pkg_resources
+
+try:
+    import importlib_metadata
+except ModuleNotFoundError:  # pragma: nocover
+    import importlib.metadata as importlib_metadata
 
 import borgmatic.actions.borg
 import borgmatic.actions.break_lock
@@ -706,7 +710,7 @@ def main():  # pragma: no cover
 
     global_arguments = arguments['global']
     if global_arguments.version:
-        print(pkg_resources.require('borgmatic')[0].version)
+        print(importlib_metadata.version('borgmatic'))
         sys.exit(0)
     if global_arguments.bash_completion:
         print(borgmatic.commands.completion.bash_completion())

+ 15 - 2
borgmatic/config/validate.py

@@ -1,9 +1,13 @@
 import os
 
 import jsonschema
-import pkg_resources
 import ruamel.yaml
 
+try:
+    import importlib_metadata
+except ModuleNotFoundError:  # pragma: nocover
+    import importlib.metadata as importlib_metadata
+
 from borgmatic.config import environment, load, normalize, override
 
 
@@ -11,8 +15,17 @@ def schema_filename():
     '''
     Path to the installed YAML configuration schema file, used to validate and parse the
     configuration.
+
+    Raise FileNotFoundError when the schema path does not exist.
     '''
-    return pkg_resources.resource_filename('borgmatic', 'config/schema.yaml')
+    try:
+        return next(
+            str(path.locate())
+            for path in importlib_metadata.files('borgmatic')
+            if path.match('config/schema.yaml')
+        )
+    except StopIteration:
+        raise FileNotFoundError('Configuration file schema could not be found')
 
 
 def format_json_error_path_element(path_element):

+ 0 - 2
setup.cfg

@@ -4,8 +4,6 @@ description_file=README.md
 [tool:pytest]
 testpaths = tests
 addopts = --cov-report term-missing:skip-covered --cov=borgmatic --ignore=tests/end-to-end
-filterwarnings = 
-    ignore:Deprecated call to `pkg_resources.declare_namespace\('ruamel'\)`.*:DeprecationWarning
 
 [flake8]
 max-line-length = 100

+ 1 - 0
setup.py

@@ -32,6 +32,7 @@ setup(
     install_requires=(
         'colorama>=0.4.1,<0.5',
         'jsonschema',
+        'packaging',
         'requests',
         'ruamel.yaml>0.15.0,<0.18.0',
         'setuptools',

+ 4 - 0
test_requirements.txt

@@ -12,8 +12,10 @@ flake8-use-fstring==1.4
 flake8-variables-names==0.0.5
 flexmock==0.11.3
 idna==3.4
+importlib_metadata==6.3.0; python_version < '3.8'
 isort==5.12.0
 mccabe==0.7.0
+packaging==23.1
 pluggy==1.0.0
 pathspec==0.11.1; python_version >= '3.8'
 py==1.11.0
@@ -27,3 +29,5 @@ requests==2.28.2
 ruamel.yaml>0.15.0,<0.18.0
 toml==0.10.2; python_version >= '3.8'
 typed-ast; python_version >= '3.8'
+typing-extensions==4.5.0; python_version < '3.8'
+zipp==3.15.0; python_version < '3.8'

+ 22 - 0
tests/unit/config/test_validate.py

@@ -4,6 +4,28 @@ from flexmock import flexmock
 from borgmatic.config import validate as module
 
 
+def test_schema_filename_finds_schema_path():
+    schema_path = '/var/borgmatic/config/schema.yaml'
+
+    flexmock(module.importlib_metadata).should_receive('files').and_return(
+        flexmock(match=lambda path: False, locate=lambda: None),
+        flexmock(match=lambda path: True, locate=lambda: schema_path),
+        flexmock(match=lambda path: False, locate=lambda: None),
+    )
+
+    assert module.schema_filename() == schema_path
+
+
+def test_schema_filename_with_missing_schema_path_raises():
+    flexmock(module.importlib_metadata).should_receive('files').and_return(
+        flexmock(match=lambda path: False, locate=lambda: None),
+        flexmock(match=lambda path: False, locate=lambda: None),
+    )
+
+    with pytest.raises(FileNotFoundError):
+        assert module.schema_filename()
+
+
 def test_format_json_error_path_element_formats_array_index():
     module.format_json_error_path_element(3) == '[3]'