[Common] Replace pkg_resources with importlib for resource path finding

With an existing Deluge package installed on the system errors were
occuring trying to start a development instance in virtualenv.

Fixed by replacing usage of deprecated pkg_resource for finding
non-python data files. Includes fallback for Python 3.7/3.8 but drops
Python 3.6 support.

The plugins are still using pkg_resources since they are distributed as
eggs and importlib extracts those data files differently to
pkg_resources so requires a different solution, either as a file stream
or manually cached when plugins are installed.

Closes: https://github.com/deluge-torrent/deluge/pull/403

Co-authored-by: DjLegolas <djlegolas@protonmail.com>
This commit is contained in:
Calum Lind 2023-05-29 12:05:56 +01:00
parent 366cded7be
commit 40a66278a3
No known key found for this signature in database
GPG Key ID: 90597A687B836BA3
4 changed files with 31 additions and 19 deletions

View File

@ -7,9 +7,6 @@ on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
env:
SETUPTOOLS_ENABLE_FEATURES: "legacy-editable"
jobs:
test-linux:
runs-on: ubuntu-20.04

View File

@ -1,5 +1,11 @@
# Changelog
## 2.2.x (TBA)
### Breaking changes
- Python 3.6 support removed (Python >= 3.7)
## 2.1.1 (2022-07-10)
### Core

View File

@ -23,15 +23,21 @@ import tarfile
import time
from contextlib import closing
from datetime import datetime
from importlib import resources
from io import BytesIO
from pathlib import Path
from urllib.parse import unquote_plus, urljoin
from urllib.request import pathname2url
import pkg_resources
from deluge.decorators import deprecated
from deluge.error import InvalidPathError
try:
from importlib.metadata import distribution
except ImportError:
from pkg_resources import get_distribution as distribution
try:
import chardet
except ImportError:
@ -90,7 +96,7 @@ def get_version():
Returns:
str: The version of Deluge.
"""
return pkg_resources.get_distribution('Deluge').version
return distribution('Deluge').version
def get_default_config_dir(filename=None):
@ -290,20 +296,22 @@ def get_pixmap(fname):
return resource_filename('deluge', os.path.join('ui', 'data', 'pixmaps', fname))
def resource_filename(module, path):
"""Get filesystem path for a resource.
def resource_filename(module: str, path: str) -> str:
"""Get filesystem path for a non-python resource.
This function contains a work-around for pkg_resources.resource_filename
not returning the correct path with multiple packages installed.
So if there's a second deluge package, installed globally and another in
develop mode somewhere else, while pkg_resources.get_distribution('Deluge')
returns the proper deluge instance, pkg_resources.resource_filename
does not, it returns the first found on the python path, which is wrong.
Abstracts getting module resource files. Originally created to
workaround pkg_resources.resource_filename limitations with
multiple Deluge packages installed.
"""
return pkg_resources.get_distribution('Deluge').get_resource_filename(
pkg_resources._manager, os.path.join(*(module.split('.') + [path]))
)
path = Path(path)
try:
with resources.as_file(resources.files(module) / path) as resource_file:
return str(resource_file)
except AttributeError:
# Python <= 3.8
with resources.path(module, path.parts[0]) as resource_file:
return str(resource_file.joinpath(*path.parts[1:]))
def open_file(path, timestamp=None):

View File

@ -486,7 +486,8 @@ class TopLevel(resource.Resource):
self.putChild(
b'ui_images',
LookupResource(
'UI_Images', common.resource_filename('deluge.ui.data', 'pixmaps')
'UI_Images',
common.resource_filename('deluge.ui', os.path.join('data', 'pixmaps')),
),
)