summaryrefslogtreecommitdiffstats
path: root/deluge/common.py
diff options
context:
space:
mode:
Diffstat (limited to 'deluge/common.py')
-rw-r--r--deluge/common.py106
1 files changed, 68 insertions, 38 deletions
diff --git a/deluge/common.py b/deluge/common.py
index 77573ffd7..be655447f 100644
--- a/deluge/common.py
+++ b/deluge/common.py
@@ -23,16 +23,22 @@ 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:
chardet = None
@@ -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.
-
- This function contains a work-around for pkg_resources.resource_filename
- not returning the correct path with multiple packages installed.
+def resource_filename(module: str, path: str) -> str:
+ """Get filesystem path for a non-python resource.
- 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):
@@ -415,25 +423,31 @@ def translate_size_units():
def fsize(fsize_b, precision=1, shortform=False):
- """Formats the bytes value into a string with KiB, MiB or GiB units.
+ """Formats the bytes value into a string with KiB, MiB, GiB or TiB units.
Args:
fsize_b (int): The filesize in bytes.
- precision (int): The filesize float precision.
+ precision (int): The output float precision, 1 by default.
+ shortform (bool): The output short|long form, False (long form) by default.
Returns:
- str: A formatted string in KiB, MiB or GiB units.
+ str: A formatted string in KiB, MiB, GiB or TiB units.
Examples:
>>> fsize(112245)
'109.6 KiB'
>>> fsize(112245, precision=0)
'110 KiB'
+ >>> fsize(112245, shortform=True)
+ '109.6 K'
Note:
This function has been refactored for performance with the
fsize units being translated outside the function.
+ Notice that short forms K|M|G|T are synonymous here with
+ KiB|MiB|GiB|TiB. They are powers of 1024, not 1000.
+
"""
if fsize_b >= 1024**4:
@@ -469,7 +483,7 @@ def fpcnt(dec, precision=2):
Args:
dec (float): The ratio in the range [0.0, 1.0].
- precision (int): The percentage float precision.
+ precision (int): The output float precision, 2 by default.
Returns:
str: A formatted string representing a percentage.
@@ -493,6 +507,8 @@ def fspeed(bps, precision=1, shortform=False):
Args:
bps (int): The speed in bytes per second.
+ precision (int): The output float precision, 1 by default.
+ shortform (bool): The output short|long form, False (long form) by default.
Returns:
str: A formatted string representing transfer speed.
@@ -501,6 +517,10 @@ def fspeed(bps, precision=1, shortform=False):
>>> fspeed(43134)
'42.1 KiB/s'
+ Note:
+ Notice that short forms K|M|G|T are synonymous here with
+ KiB|MiB|GiB|TiB. They are powers of 1024, not 1000.
+
"""
if bps < 1024**2:
@@ -537,7 +557,7 @@ def fpeer(num_peers, total_peers):
total_peers (int): The total number of peers.
Returns:
- str: A formatted string 'num_peers (total_peers)' or total_peers < 0, just 'num_peers'.
+ str: A formatted string 'num_peers (total_peers)' or if total_peers < 0, just 'num_peers'.
Examples:
>>> fpeer(10, 20)
@@ -586,16 +606,16 @@ def ftime(secs):
time_str = f'{secs // 604800}w {secs // 86400 % 7}d'
else:
time_str = f'{secs // 31449600}y {secs // 604800 % 52}w'
-
return time_str
def fdate(seconds, date_only=False, precision_secs=False):
- """Formats a date time string in the locale's date representation based on the systems timezone.
+ """Formats a date time string in the locale's date representation based on the system's timezone.
Args:
seconds (float): Time in seconds since the Epoch.
- precision_secs (bool): Include seconds in time format.
+ date_only (bool): Whether to include only the date, False by default.
+ precision_secs (bool): Include seconds in time format, False by default.
Returns:
str: A string in the locale's datetime representation or "" if seconds < 0
@@ -620,10 +640,14 @@ def tokenize(text):
Returns:
list: A list of strings and/or numbers.
- This function is used to implement robust tokenization of user input
- It automatically coerces integer and floating point numbers, ignores
- whitespace and knows how to separate numbers from strings even without
- whitespace.
+ Note:
+ This function is used to implement robust tokenization of user input
+ It automatically coerces integer and floating point numbers, ignores
+ whitespace and knows how to separate numbers from strings even without
+ whitespace.
+
+ Possible optimization: move the 2 regexes outside of function.
+
"""
tokenized_input = []
for token in re.split(r'(\d+(?:\.\d+)?)', text):
@@ -644,12 +668,16 @@ size_units = [
{'prefix': 'GiB', 'divider': 1024**3},
{'prefix': 'TiB', 'divider': 1024**4},
{'prefix': 'PiB', 'divider': 1024**5},
+ {'prefix': 'k', 'divider': 1000**1},
+ {'prefix': 'm', 'divider': 1000**2},
+ {'prefix': 'g', 'divider': 1000**3},
+ {'prefix': 't', 'divider': 1000**4},
+ {'prefix': 'p', 'divider': 1000**5},
{'prefix': 'KB', 'divider': 1000**1},
{'prefix': 'MB', 'divider': 1000**2},
{'prefix': 'GB', 'divider': 1000**3},
{'prefix': 'TB', 'divider': 1000**4},
{'prefix': 'PB', 'divider': 1000**5},
- {'prefix': 'm', 'divider': 1000**2},
]
@@ -734,6 +762,8 @@ MAGNET_SCHEME = 'magnet:?'
XT_BTIH_PARAM = 'xt=urn:btih:'
DN_PARAM = 'dn='
TR_PARAM = 'tr='
+TR_TIER_PARAM = 'tr.'
+TR_TIER_REGEX = re.compile(r'^tr.(\d+)=(\S+)')
def is_magnet(uri):
@@ -776,8 +806,6 @@ def get_magnet_info(uri):
"""
- tr0_param = 'tr.'
- tr0_param_regex = re.compile(r'^tr.(\d+)=(\S+)')
if not uri.startswith(MAGNET_SCHEME):
return {}
@@ -805,12 +833,14 @@ def get_magnet_info(uri):
tracker = unquote_plus(param[len(TR_PARAM) :])
trackers[tracker] = tier
tier += 1
- elif param.startswith(tr0_param):
- try:
- tier, tracker = re.match(tr0_param_regex, param).groups()
- trackers[tracker] = tier
- except AttributeError:
- pass
+ elif param.startswith(TR_TIER_PARAM):
+ tracker_match = re.match(TR_TIER_REGEX, param)
+ if not tracker_match:
+ continue
+
+ tier, tracker = tracker_match.groups()
+ tracker = unquote_plus(tracker)
+ trackers[tracker] = int(tier)
if info_hash:
if not name:
@@ -831,7 +861,7 @@ def create_magnet_uri(infohash, name=None, trackers=None):
Args:
infohash (str): The info-hash of the torrent.
name (str, optional): The name of the torrent.
- trackers (list or dict, optional): A list of trackers or dict or {tracker: tier} pairs.
+ trackers (list or dict, optional): A list of trackers or a dict or some {tracker: tier} pairs.
Returns:
str: A magnet URI string.
@@ -873,7 +903,7 @@ def get_path_size(path):
return os.path.getsize(path)
dir_size = 0
- for (p, dummy_dirs, files) in os.walk(path):
+ for p, dummy_dirs, files in os.walk(path):
for _file in files:
filename = os.path.join(p, _file)
dir_size += os.path.getsize(filename)