automated upgrade of python code using ruff and ./setup.py upgrade_source_code

This commit is contained in:
Kovid Goyal 2025-01-22 09:44:19 +05:30
parent dd71135591
commit 3e58252176
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
47 changed files with 129 additions and 173 deletions

View File

@ -3,7 +3,6 @@ requires-python = ">=3.10"
[tool.ruff] [tool.ruff]
line-length = 160 line-length = 160
target-version = 'py310'
builtins = ['_', 'I', 'P'] builtins = ['_', 'I', 'P']
include = ['*.py', '*.recipe'] include = ['*.py', '*.recipe']
exclude = [ exclude = [

View File

@ -5,7 +5,6 @@ afriquexxi.info
''' '''
from datetime import datetime from datetime import datetime
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from calibre import browser from calibre import browser

View File

@ -3,9 +3,9 @@
import json import json
from datetime import datetime from datetime import datetime
from zoneinfo import ZoneInfo
import mechanize import mechanize
from zoneinfo import ZoneInfo
from calibre import browser from calibre import browser
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe

View File

@ -5,7 +5,6 @@ contretemps.eu
''' '''
from datetime import datetime from datetime import datetime
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from calibre.web.feeds.news import BasicNewsRecipe from calibre.web.feeds.news import BasicNewsRecipe

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
''' orientxxi.info ''' ''' orientxxi.info '''
from datetime import datetime from datetime import datetime
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from calibre import browser from calibre import browser

View File

@ -15,7 +15,7 @@ import sys
import sysconfig import sysconfig
import textwrap import textwrap
from functools import partial from functools import partial
from typing import List, NamedTuple from typing import NamedTuple
from setup import SRC, Command, isbsd, isfreebsd, ishaiku, islinux, ismacos, iswindows from setup import SRC, Command, isbsd, isfreebsd, ishaiku, islinux, ismacos, iswindows
@ -24,14 +24,14 @@ isunix = islinux or ismacos or isbsd or ishaiku
py_lib = os.path.join(sys.prefix, 'libs', 'python%d%d.lib' % sys.version_info[:2]) py_lib = os.path.join(sys.prefix, 'libs', 'python%d%d.lib' % sys.version_info[:2])
class CompileCommand(NamedTuple): class CompileCommand(NamedTuple):
cmd: List[str] cmd: list[str]
src: str src: str
dest: str dest: str
class LinkCommand(NamedTuple): class LinkCommand(NamedTuple):
cmd: List[str] cmd: list[str]
objects: List[str] objects: list[str]
dest: str dest: str
@ -209,11 +209,11 @@ class Environment(NamedTuple):
cc: str cc: str
cxx: str cxx: str
linker: str linker: str
base_cflags: List[str] base_cflags: list[str]
base_cxxflags: List[str] base_cxxflags: list[str]
base_ldflags: List[str] base_ldflags: list[str]
cflags: List[str] cflags: list[str]
ldflags: List[str] ldflags: list[str]
make: str make: str
internal_inc_prefix: str internal_inc_prefix: str
external_inc_prefix: str external_inc_prefix: str
@ -228,10 +228,10 @@ class Environment(NamedTuple):
dest_ext: str dest_ext: str
std_prefix: str std_prefix: str
def inc_dirs_to_cflags(self, dirs) -> List[str]: def inc_dirs_to_cflags(self, dirs) -> list[str]:
return [self.external_inc_prefix+x for x in dirs] return [self.external_inc_prefix+x for x in dirs]
def lib_dirs_to_ldflags(self, dirs) -> List[str]: def lib_dirs_to_ldflags(self, dirs) -> list[str]:
return [self.libdir_prefix+x for x in dirs if x] return [self.libdir_prefix+x for x in dirs if x]
def libraries_to_ldflags(self, libs): def libraries_to_ldflags(self, libs):

View File

@ -1,4 +1,3 @@
# -* coding: utf-8 -*-
# #
# License: MIT (see LICENSE file provided) # License: MIT (see LICENSE file provided)
# vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: # vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4:
@ -34,24 +33,13 @@ default_encoding = 'utf-8'
# python 2/3 compatibility helpers {{{ # python 2/3 compatibility helpers {{{
if sys.version_info < (3,): PY3 = True
PY3 = False text_type = str
text_type = unicode
def b(s): def b(s):
return s return s.encode()
def u(s): def u(s):
return unicode(s, "unicode_escape")
else:
PY3 = True
text_type = str
def b(s):
return s.encode("latin-1")
def u(s):
return s return s
# }}} # }}}
# _pofile_or_mofile {{{ # _pofile_or_mofile {{{
@ -390,7 +378,7 @@ class _BaseFile(list):
# But if pickling, we never want to check for duplicates anyway. # But if pickling, we never want to check for duplicates anyway.
if getattr(self, 'check_for_duplicates', False) and entry in self: if getattr(self, 'check_for_duplicates', False) and entry in self:
raise ValueError('Entry "%s" already exists' % entry.msgid) raise ValueError('Entry "%s" already exists' % entry.msgid)
super(_BaseFile, self).append(entry) super().append(entry)
def insert(self, index, entry): def insert(self, index, entry):
""" """
@ -408,7 +396,7 @@ class _BaseFile(list):
""" """
if self.check_for_duplicates and entry in self: if self.check_for_duplicates and entry in self:
raise ValueError('Entry "%s" already exists' % entry.msgid) raise ValueError('Entry "%s" already exists' % entry.msgid)
super(_BaseFile, self).insert(index, entry) super().insert(index, entry)
def metadata_as_entry(self): def metadata_as_entry(self):
""" """
@ -420,7 +408,7 @@ class _BaseFile(list):
strs = [] strs = []
for name, value in mdata: for name, value in mdata:
# Strip whitespace off each line in a multi-line entry # Strip whitespace off each line in a multi-line entry
strs.append('%s: %s' % (name, value)) strs.append(f'{name}: {value}')
e.msgstr = '\n'.join(strs) + '\n' e.msgstr = '\n'.join(strs) + '\n'
if self.metadata_is_fuzzy: if self.metadata_is_fuzzy:
e.flags.append('fuzzy') e.flags.append('fuzzy')
@ -444,7 +432,7 @@ class _BaseFile(list):
string, controls how universal newlines works string, controls how universal newlines works
""" """
if self.fpath is None and fpath is None: if self.fpath is None and fpath is None:
raise IOError('You must provide a file path to save() method') raise OSError('You must provide a file path to save() method')
contents = getattr(self, repr_method)() contents = getattr(self, repr_method)()
if fpath is None: if fpath is None:
fpath = self.fpath fpath = self.fpath
@ -452,7 +440,7 @@ class _BaseFile(list):
with open(fpath, 'wb') as fhandle: with open(fpath, 'wb') as fhandle:
fhandle.write(contents) fhandle.write(contents)
else: else:
with io.open( with open(
fpath, fpath,
'w', 'w',
encoding=self.encoding, encoding=self.encoding,
@ -730,10 +718,10 @@ class POFile(_BaseFile):
object POFile, the reference catalog. object POFile, the reference catalog.
""" """
# Store entries in dict/set for faster access # Store entries in dict/set for faster access
self_entries = dict( self_entries = {
(entry.msgid_with_context, entry) for entry in self entry.msgid_with_context: entry for entry in self
) }
refpot_msgids = set(entry.msgid_with_context for entry in refpot) refpot_msgids = {entry.msgid_with_context for entry in refpot}
# Merge entries that are in the refpot # Merge entries that are in the refpot
for entry in refpot: for entry in refpot:
e = self_entries.get(entry.msgid_with_context) e = self_entries.get(entry.msgid_with_context)
@ -822,7 +810,7 @@ class MOFile(_BaseFile):
# class _BaseEntry {{{ # class _BaseEntry {{{
class _BaseEntry(object): class _BaseEntry:
""" """
Base class for :class:`~polib.POEntry` and :class:`~polib.MOEntry` classes. Base class for :class:`~polib.POEntry` and :class:`~polib.MOEntry` classes.
This class should **not** be instantiated directly. This class should **not** be instantiated directly.
@ -942,16 +930,16 @@ class _BaseEntry(object):
# quick and dirty trick to get the real field name # quick and dirty trick to get the real field name
fieldname = fieldname[9:] fieldname = fieldname[9:]
ret = ['%s%s%s "%s"' % (delflag, fieldname, plural_index, ret = ['{}{}{} "{}"'.format(delflag, fieldname, plural_index,
escape(lines.pop(0)))] escape(lines.pop(0)))]
for line in lines: for line in lines:
ret.append('%s"%s"' % (delflag, escape(line))) ret.append(f'{delflag}"{escape(line)}"')
return ret return ret
@property @property
def msgid_with_context(self): def msgid_with_context(self):
if self.msgctxt: if self.msgctxt:
return '%s%s%s' % (self.msgctxt, "\x04", self.msgid) return '{}{}{}'.format(self.msgctxt, "\x04", self.msgid)
return self.msgid return self.msgid
# }}} # }}}
# class POEntry {{{ # class POEntry {{{
@ -1023,14 +1011,14 @@ class POEntry(_BaseEntry):
break_long_words=False break_long_words=False
) )
else: else:
ret.append('%s%s' % (c[1], comment)) ret.append(f'{c[1]}{comment}')
# occurrences (with text wrapping as xgettext does) # occurrences (with text wrapping as xgettext does)
if not self.obsolete and self.occurrences: if not self.obsolete and self.occurrences:
filelist = [] filelist = []
for fpath, lineno in self.occurrences: for fpath, lineno in self.occurrences:
if lineno: if lineno:
filelist.append('%s:%s' % (fpath, lineno)) filelist.append(f'{fpath}:{lineno}')
else: else:
filelist.append(fpath) filelist.append(fpath)
filestr = ' '.join(filelist) filestr = ' '.join(filelist)
@ -1238,7 +1226,7 @@ class MOEntry(_BaseEntry):
# class _POFileParser {{{ # class _POFileParser {{{
class _POFileParser(object): class _POFileParser:
""" """
A finite state machine to efficiently and correctly parse po A finite state machine to efficiently and correctly parse po
file format. file format.
@ -1264,10 +1252,10 @@ class _POFileParser(object):
enc = kwargs.get('encoding', default_encoding) enc = kwargs.get('encoding', default_encoding)
if _is_file(pofile): if _is_file(pofile):
try: try:
self.fhandle = io.open(pofile, 'rt', encoding=enc) self.fhandle = open(pofile, encoding=enc)
except LookupError: except LookupError:
enc = default_encoding enc = default_encoding
self.fhandle = io.open(pofile, 'rt', encoding=enc) self.fhandle = open(pofile, encoding=enc)
else: else:
self.fhandle = pofile.splitlines() self.fhandle = pofile.splitlines()
@ -1373,7 +1361,7 @@ class _POFileParser(object):
if tokens[0] in keywords and nb_tokens > 1: if tokens[0] in keywords and nb_tokens > 1:
line = line[len(tokens[0]):].lstrip() line = line[len(tokens[0]):].lstrip()
if re.search(r'([^\\]|^)"', line[1:-1]): if re.search(r'([^\\]|^)"', line[1:-1]):
raise IOError('Syntax error in po file %s(line %s): ' raise OSError('Syntax error in po file %s(line %s): '
'unescaped double quote found' % 'unescaped double quote found' %
(fpath, self.current_line)) (fpath, self.current_line))
self.current_token = line self.current_token = line
@ -1391,7 +1379,7 @@ class _POFileParser(object):
elif line[:1] == '"': elif line[:1] == '"':
# we are on a continuation line # we are on a continuation line
if re.search(r'([^\\]|^)"', line[1:-1]): if re.search(r'([^\\]|^)"', line[1:-1]):
raise IOError('Syntax error in po file %s(line %s): ' raise OSError('Syntax error in po file %s(line %s): '
'unescaped double quote found' % 'unescaped double quote found' %
(fpath, self.current_line)) (fpath, self.current_line))
self.process('mc') self.process('mc')
@ -1420,7 +1408,7 @@ class _POFileParser(object):
elif tokens[0] == '#|': elif tokens[0] == '#|':
if nb_tokens <= 1: if nb_tokens <= 1:
raise IOError('Syntax error in po file %s(line %s)' % raise OSError('Syntax error in po file %s(line %s)' %
(fpath, self.current_line)) (fpath, self.current_line))
# Remove the marker and any whitespace right after that. # Remove the marker and any whitespace right after that.
@ -1434,14 +1422,14 @@ class _POFileParser(object):
if nb_tokens == 2: if nb_tokens == 2:
# Invalid continuation line. # Invalid continuation line.
raise IOError('Syntax error in po file %s(line %s): ' raise OSError('Syntax error in po file %s(line %s): '
'invalid continuation line' % 'invalid continuation line' %
(fpath, self.current_line)) (fpath, self.current_line))
# we are on a "previous translation" comment line, # we are on a "previous translation" comment line,
if tokens[1] not in prev_keywords: if tokens[1] not in prev_keywords:
# Unknown keyword in previous translation comment. # Unknown keyword in previous translation comment.
raise IOError('Syntax error in po file %s(line %s): ' raise OSError('Syntax error in po file %s(line %s): '
'unknown keyword %s' % 'unknown keyword %s' %
(fpath, self.current_line, (fpath, self.current_line,
tokens[1])) tokens[1]))
@ -1453,7 +1441,7 @@ class _POFileParser(object):
self.process(prev_keywords[tokens[1]]) self.process(prev_keywords[tokens[1]])
else: else:
raise IOError('Syntax error in po file %s(line %s)' % raise OSError('Syntax error in po file %s(line %s)' %
(fpath, self.current_line)) (fpath, self.current_line))
if self.current_entry and len(tokens) > 0 and \ if self.current_entry and len(tokens) > 0 and \
@ -1524,7 +1512,7 @@ class _POFileParser(object):
fpath = '%s ' % self.instance.fpath if self.instance.fpath else '' fpath = '%s ' % self.instance.fpath if self.instance.fpath else ''
if hasattr(self.fhandle, 'close'): if hasattr(self.fhandle, 'close'):
self.fhandle.close() self.fhandle.close()
raise IOError('Syntax error in po file %s(line %s)' % raise OSError('Syntax error in po file %s(line %s)' %
(fpath, self.current_line)) (fpath, self.current_line))
# state handlers # state handlers
@ -1673,7 +1661,7 @@ class _POFileParser(object):
# class _MOFileParser {{{ # class _MOFileParser {{{
class _MOFileParser(object): class _MOFileParser:
""" """
A class to parse binary mo files. A class to parse binary mo files.
""" """
@ -1729,14 +1717,14 @@ class _MOFileParser(object):
elif magic_number == MOFile.MAGIC_SWAPPED: elif magic_number == MOFile.MAGIC_SWAPPED:
ii = '>II' ii = '>II'
else: else:
raise IOError('Invalid mo file, magic number is incorrect !') raise OSError('Invalid mo file, magic number is incorrect !')
self.instance.magic_number = magic_number self.instance.magic_number = magic_number
# parse the version number and the number of strings # parse the version number and the number of strings
version, numofstrings = self._readbinary(ii, 8) version, numofstrings = self._readbinary(ii, 8)
# from MO file format specs: "A program seeing an unexpected major # from MO file format specs: "A program seeing an unexpected major
# revision number should stop reading the MO file entirely" # revision number should stop reading the MO file entirely"
if version >> 16 not in (0, 1): if version >> 16 not in (0, 1):
raise IOError('Invalid mo file, unexpected major revision number') raise OSError('Invalid mo file, unexpected major revision number')
self.instance.version = version self.instance.version = version
# original strings and translation strings hash table offset # original strings and translation strings hash table offset
msgids_hash_offset, msgstrs_hash_offset = self._readbinary(ii, 8) msgids_hash_offset, msgstrs_hash_offset = self._readbinary(ii, 8)
@ -1777,8 +1765,8 @@ class _MOFileParser(object):
entry = self._build_entry( entry = self._build_entry(
msgid=msgid_tokens[0], msgid=msgid_tokens[0],
msgid_plural=msgid_tokens[1], msgid_plural=msgid_tokens[1],
msgstr_plural=dict((k, v) for k, v in msgstr_plural={k: v for k, v in
enumerate(msgstr.split(b('\0')))) enumerate(msgstr.split(b('\0')))}
) )
else: else:
entry = self._build_entry(msgid=msgid, msgstr=msgstr) entry = self._build_entry(msgid=msgid, msgstr=msgstr)

View File

@ -17,7 +17,6 @@ import time
import uuid import uuid
from contextlib import closing, suppress from contextlib import closing, suppress
from functools import partial from functools import partial
from typing import Optional
import apsw import apsw
@ -1007,7 +1006,7 @@ class DB:
def add_notes_resource(self, path_or_stream, name, mtime=None) -> int: def add_notes_resource(self, path_or_stream, name, mtime=None) -> int:
return self.notes.add_resource(self.conn, path_or_stream, name, mtime=mtime) return self.notes.add_resource(self.conn, path_or_stream, name, mtime=mtime)
def get_notes_resource(self, resource_hash) -> Optional[dict]: def get_notes_resource(self, resource_hash) -> dict | None:
return self.notes.get_resource_data(self.conn, resource_hash) return self.notes.get_resource_data(self.conn, resource_hash)
def notes_resources_used_by(self, field, item_id): def notes_resources_used_by(self, field, item_id):

View File

@ -14,13 +14,13 @@ import sys
import traceback import traceback
import weakref import weakref
from collections import defaultdict from collections import defaultdict
from collections.abc import MutableSet, Set from collections.abc import Iterable, MutableSet, Set
from functools import partial, wraps from functools import partial, wraps
from io import DEFAULT_BUFFER_SIZE, BytesIO from io import DEFAULT_BUFFER_SIZE, BytesIO
from queue import Queue from queue import Queue
from threading import Lock from threading import Lock
from time import mktime, monotonic, sleep, time from time import mktime, monotonic, sleep, time
from typing import Iterable, NamedTuple, Optional, Tuple from typing import NamedTuple
from calibre import as_unicode, detect_ncpus, isbytestring from calibre import as_unicode, detect_ncpus, isbytestring
from calibre.constants import iswindows, preferred_encoding from calibre.constants import iswindows, preferred_encoding
@ -725,7 +725,7 @@ class Cache:
return self.backend.add_notes_resource(path_or_stream_or_data, name, mtime) return self.backend.add_notes_resource(path_or_stream_or_data, name, mtime)
@read_api @read_api
def get_notes_resource(self, resource_hash) -> Optional[dict]: def get_notes_resource(self, resource_hash) -> dict | None:
' Return a dict containing the resource data and name or None if no resource with the specified hash is found ' ' Return a dict containing the resource data and name or None if no resource with the specified hash is found '
return self.backend.get_notes_resource(resource_hash) return self.backend.get_notes_resource(resource_hash)
@ -3370,7 +3370,7 @@ class Cache:
return dict.fromkeys(relpaths) return dict.fromkeys(relpaths)
@read_api @read_api
def list_extra_files(self, book_id, use_cache=False, pattern='') -> Tuple[ExtraFile, ...]: def list_extra_files(self, book_id, use_cache=False, pattern='') -> tuple[ExtraFile, ...]:
''' '''
Get information about extra files in the book's directory. Get information about extra files in the book's directory.

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
# License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net> # License: GPLv3 Copyright: 2023, Kovid Goyal <kovid at kovidgoyal.net>
from collections.abc import Sequence
from dataclasses import dataclass from dataclasses import dataclass
from typing import Sequence
COVER_FILE_NAME = 'cover.jpg' COVER_FILE_NAME = 'cover.jpg'
METADATA_FILE_NAME = 'metadata.opf' METADATA_FILE_NAME = 'metadata.opf'

View File

@ -7,9 +7,9 @@ __docformat__ = 'restructuredtext en'
import sys import sys
from collections import Counter, defaultdict from collections import Counter, defaultdict
from collections.abc import Iterable
from functools import partial from functools import partial
from threading import Lock from threading import Lock
from typing import Iterable
from calibre.db.tables import MANY_MANY, MANY_ONE, ONE_ONE, null from calibre.db.tables import MANY_MANY, MANY_ONE, ONE_ONE, null
from calibre.db.utils import atof, force_to_bool from calibre.db.utils import atof, force_to_bool

View File

@ -8,7 +8,6 @@ import time
from collections import defaultdict from collections import defaultdict
from contextlib import suppress from contextlib import suppress
from itertools import count, repeat from itertools import count, repeat
from typing import Optional
import apsw import apsw
import xxhash import xxhash
@ -368,7 +367,7 @@ class Notes:
name = f'{base_name}-{c}{ext}' name = f'{base_name}-{c}{ext}'
return resource_hash return resource_hash
def get_resource_data(self, conn, resource_hash) -> Optional[dict]: def get_resource_data(self, conn, resource_hash) -> dict | None:
ans = None ans = None
for (name,) in conn.execute('SELECT name FROM notes_db.resources WHERE hash=?', (resource_hash,)): for (name,) in conn.execute('SELECT name FROM notes_db.resources WHERE hash=?', (resource_hash,)):
path = self.path_for_resource(resource_hash) path = self.path_for_resource(resource_hash)

View File

@ -7,8 +7,8 @@ __docformat__ = 'restructuredtext en'
import numbers import numbers
from collections import defaultdict from collections import defaultdict
from collections.abc import Iterable
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Iterable
from calibre.ebooks.metadata import author_to_author_sort from calibre.ebooks.metadata import author_to_author_sort
from calibre.utils.date import UNDEFINED_DATE, parse_date, utc_tz from calibre.utils.date import UNDEFINED_DATE, parse_date, utc_tz

View File

@ -2,7 +2,6 @@ __license__ = 'GPL v3'
__copyright__ = '2011, John Schember <john at nachtimwald.com>, refactored: 2022, Vaso Peras-Likodric <vaso at vipl.in.rs>' __copyright__ = '2011, John Schember <john at nachtimwald.com>, refactored: 2022, Vaso Peras-Likodric <vaso at vipl.in.rs>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from typing import Dict, Optional
''' '''
Generates and writes an APNX page mapping file. Generates and writes an APNX page mapping file.
@ -29,14 +28,14 @@ class APNXBuilder:
Create an APNX file using a pseudo page mapping. Create an APNX file using a pseudo page mapping.
""" """
generators: Dict[str, IPageGenerator] = { generators: dict[str, IPageGenerator] = {
FastPageGenerator.instance.name(): FastPageGenerator.instance, FastPageGenerator.instance.name(): FastPageGenerator.instance,
AccuratePageGenerator.instance.name(): AccuratePageGenerator.instance, AccuratePageGenerator.instance.name(): AccuratePageGenerator.instance,
PagebreakPageGenerator.instance.name(): PagebreakPageGenerator.instance, PagebreakPageGenerator.instance.name(): PagebreakPageGenerator.instance,
# ExactPageGenerator.instance.name(): ExactPageGenerator.instance, # ExactPageGenerator.instance.name(): ExactPageGenerator.instance,
} }
def write_apnx(self, mobi_file_path: str, apnx_path: str, method: Optional[str] = None, page_count: int = 0): def write_apnx(self, mobi_file_path: str, apnx_path: str, method: str | None = None, page_count: int = 0):
""" """
If you want a fixed number of pages (such as from a custom column) then If you want a fixed number of pages (such as from a custom column) then
pass in a value to page_count, otherwise a count will be estimated pass in a value to page_count, otherwise a count will be estimated
@ -61,7 +60,7 @@ class APNXBuilder:
fsync(apnxf) fsync(apnxf)
@staticmethod @staticmethod
def get_apnx_meta(mobi_file_path) -> Dict[str, str]: def get_apnx_meta(mobi_file_path) -> dict[str, str]:
import uuid import uuid
apnx_meta = { apnx_meta = {
'guid': str(uuid.uuid4()).replace('-', '')[:8], 'guid': str(uuid.uuid4()).replace('-', '')[:8],

View File

@ -2,7 +2,6 @@ __license__ = 'GPL v3'
__copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>' __copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from typing import Optional
from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator
from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html
@ -16,10 +15,10 @@ class AccuratePageGenerator(IPageGenerator):
def name(self) -> str: def name(self) -> str:
return "accurate" return "accurate"
def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages:
return FastPageGenerator.instance.generate(mobi_file_path, real_count) return FastPageGenerator.instance.generate(mobi_file_path, real_count)
def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages:
""" """
A more accurate but much more resource intensive and slower A more accurate but much more resource intensive and slower
method to calculate the page length. method to calculate the page length.

View File

@ -2,7 +2,6 @@ __license__ = 'GPL v3'
__copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>' __copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from typing import Optional
from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator
from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html_length from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html_length
@ -16,10 +15,10 @@ class ExactPageGenerator(IPageGenerator):
def name(self) -> str: def name(self) -> str:
return "exact" return "exact"
def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages:
return FastPageGenerator.instance.generate(mobi_file_path, real_count) return FastPageGenerator.instance.generate(mobi_file_path, real_count)
def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages:
""" """
Given a specified page count (such as from a custom column), Given a specified page count (such as from a custom column),
create our array of pages for the apnx file by dividing by create our array of pages for the apnx file by dividing by

View File

@ -2,7 +2,6 @@ __license__ = 'GPL v3'
__copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>' __copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from typing import Optional
from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html_length from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html_length
from calibre.devices.kindle.apnx_page_generator.pages import Pages from calibre.devices.kindle.apnx_page_generator.pages import Pages
@ -13,10 +12,10 @@ class FastPageGenerator(IPageGenerator):
def name(self) -> str: def name(self) -> str:
return "fast" return "fast"
def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages:
raise Exception("Fast calculation impossible.") raise Exception("Fast calculation impossible.")
def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages:
""" """
2300 characters of uncompressed text per page. This is 2300 characters of uncompressed text per page. This is
not meant to map 1 to 1 to a print book but to be a not meant to map 1 to 1 to a print book but to be a

View File

@ -3,7 +3,6 @@ __copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import re import re
from typing import Optional
from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator from calibre.devices.kindle.apnx_page_generator.generators.fast_page_generator import FastPageGenerator
from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html from calibre.devices.kindle.apnx_page_generator.i_page_generator import IPageGenerator, mobi_html
@ -15,10 +14,10 @@ class PagebreakPageGenerator(IPageGenerator):
def name(self) -> str: def name(self) -> str:
return "pagebreak" return "pagebreak"
def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages:
return FastPageGenerator.instance.generate(mobi_file_path, real_count) return FastPageGenerator.instance.generate(mobi_file_path, real_count)
def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages:
""" Determine pages based on the presence of <*pagebreak*/>. """ """ Determine pages based on the presence of <*pagebreak*/>. """
html = mobi_html(mobi_file_path) html = mobi_html(mobi_file_path)
pages = [] pages = []

View File

@ -4,7 +4,6 @@ __docformat__ = 'restructuredtext en'
import struct import struct
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from typing import Optional
from calibre.devices.kindle.apnx_page_generator.pages import Pages from calibre.devices.kindle.apnx_page_generator.pages import Pages
from calibre.ebooks.pdb.header import PdbHeaderReader from calibre.ebooks.pdb.header import PdbHeaderReader
@ -15,14 +14,14 @@ from polyglot.builtins import as_bytes
class IPageGenerator(metaclass=ABCMeta): class IPageGenerator(metaclass=ABCMeta):
@abstractmethod @abstractmethod
def _generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate(self, mobi_file_path: str, real_count: int | None) -> Pages:
pass pass
@abstractmethod @abstractmethod
def _generate_fallback(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def _generate_fallback(self, mobi_file_path: str, real_count: int | None) -> Pages:
pass pass
def generate(self, mobi_file_path: str, real_count: Optional[int]) -> Pages: def generate(self, mobi_file_path: str, real_count: int | None) -> Pages:
try: try:
result = self._generate(mobi_file_path, real_count) result = self._generate(mobi_file_path, real_count)
if result.number_of_pages > 0: if result.number_of_pages > 0:

View File

@ -2,32 +2,31 @@ __license__ = 'GPL v3'
__copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>' __copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from typing import List, Tuple, Union
from calibre.devices.kindle.apnx_page_generator.page_number_type import PageNumberTypes from calibre.devices.kindle.apnx_page_generator.page_number_type import PageNumberTypes
class PageGroup: class PageGroup:
"""Simulate constructor overloading""" """Simulate constructor overloading"""
def __init__(self, page_locations: Union[int, List[int]], page_number_type: PageNumberTypes, first_value: int, def __init__(self, page_locations: int | list[int], page_number_type: PageNumberTypes, first_value: int,
page_labels: Union[str, List[str], None] = None): page_labels: str | list[str] | None = None):
if page_locations.__class__ is int: if page_locations.__class__ is int:
self.page_locations: List[int] = [page_locations] self.page_locations: list[int] = [page_locations]
else: else:
self.page_locations: List[int] = page_locations self.page_locations: list[int] = page_locations
self.__page_number_type: PageNumberTypes = page_number_type self.__page_number_type: PageNumberTypes = page_number_type
self.__first_value = first_value self.__first_value = first_value
if page_number_type == PageNumberTypes.Custom: if page_number_type == PageNumberTypes.Custom:
assert page_labels is not None assert page_labels is not None
if page_labels.__class__ is str: if page_labels.__class__ is str:
assert 1 == len(self.page_locations) and len(page_labels) > 0 assert 1 == len(self.page_locations) and len(page_labels) > 0
self.__page_number_labels: List[str] = [page_labels] self.__page_number_labels: list[str] = [page_labels]
else: else:
assert len(page_labels) == len(self.page_locations) assert len(page_labels) == len(self.page_locations)
assert all(len(label) > 0 for label in page_labels) assert all(len(label) > 0 for label in page_labels)
self.__page_number_labels: List[str] = page_labels self.__page_number_labels: list[str] = page_labels
def append(self, page_location: Union[int, Tuple[int, str]]) -> None: def append(self, page_location: int | tuple[int, str]) -> None:
if page_location.__class__ is int: if page_location.__class__ is int:
assert self.__page_number_type != PageNumberTypes.Custom assert self.__page_number_type != PageNumberTypes.Custom
self.page_locations.append(page_location) self.page_locations.append(page_location)
@ -54,4 +53,4 @@ class PageGroup:
values = str(self.__first_value) values = str(self.__first_value)
else: else:
values = "|".join(self.__page_number_labels) values = "|".join(self.__page_number_labels)
return "({},{},{})".format(starting_location, self.__page_number_type.value, values) return f"({starting_location},{self.__page_number_type.value},{values})"

View File

@ -3,18 +3,17 @@ __copyright__ = '2022, Vaso Peras-Likodric <vaso at vipl.in.rs>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import itertools import itertools
from typing import List, Optional
from calibre.devices.kindle.apnx_page_generator.page_group import PageGroup from calibre.devices.kindle.apnx_page_generator.page_group import PageGroup
from calibre.devices.kindle.apnx_page_generator.page_number_type import PageNumberTypes from calibre.devices.kindle.apnx_page_generator.page_number_type import PageNumberTypes
class Pages: class Pages:
def __init__(self, page_locations: Optional[List[int]] = None): def __init__(self, page_locations: list[int] | None = None):
if page_locations.__class__ is list: if page_locations.__class__ is list:
self.__pages_groups: List[PageGroup] = [PageGroup(page_locations, PageNumberTypes.Arabic, 1)] self.__pages_groups: list[PageGroup] = [PageGroup(page_locations, PageNumberTypes.Arabic, 1)]
else: else:
self.__pages_groups: List[PageGroup] = [] self.__pages_groups: list[PageGroup] = []
def append(self, page_location: PageGroup) -> None: def append(self, page_location: PageGroup) -> None:
self.__pages_groups.append(page_location) self.__pages_groups.append(page_location)
@ -34,7 +33,7 @@ class Pages:
return ",".join(result) return ",".join(result)
@property @property
def page_locations(self) -> List[int]: def page_locations(self) -> list[int]:
return list(itertools.chain.from_iterable(list(map(lambda pg: pg.page_locations, self.__pages_groups)))) return list(itertools.chain.from_iterable(list(map(lambda pg: pg.page_locations, self.__pages_groups))))
@property @property

View File

@ -12,8 +12,9 @@ import os
import posixpath import posixpath
import sys import sys
import traceback import traceback
from collections.abc import Sequence
from io import BytesIO from io import BytesIO
from typing import NamedTuple, Sequence from typing import NamedTuple
from calibre import prints from calibre import prints
from calibre.constants import iswindows, numeric_version from calibre.constants import iswindows, numeric_version

View File

@ -13,7 +13,6 @@ from collections import defaultdict, deque
from datetime import datetime from datetime import datetime
from itertools import chain from itertools import chain
from operator import attrgetter from operator import attrgetter
from typing import Dict, Tuple
from calibre import force_unicode, human_readable, prints from calibre import force_unicode, human_readable, prints
from calibre.constants import iswindows from calibre.constants import iswindows
@ -121,7 +120,7 @@ class FileOrFolder:
return not self.files and not self.folders return not self.files and not self.folders
@property @property
def id_map(self) -> Dict[int, 'FileOrFolder']: def id_map(self) -> dict[int, 'FileOrFolder']:
return self.fs_cache().id_maps[self.storage_id] return self.fs_cache().id_maps[self.storage_id]
@property @property
@ -141,7 +140,7 @@ class FileOrFolder:
return self.fs_cache().storage(self.storage_id) return self.fs_cache().storage(self.storage_id)
@property @property
def full_path(self) -> Tuple[str, ...]: def full_path(self) -> tuple[str, ...]:
parts = deque() parts = deque()
parts.append(self.name) parts.append(self.name)
p = self.parent p = self.parent

View File

@ -159,7 +159,7 @@ class ConnectionListener(Thread):
device_socket = None device_socket = None
self.driver._debug('driver is not answering') self.driver._debug('driver is not answering')
except socket.timeout: except TimeoutError:
pass pass
except OSError: except OSError:
x = sys.exc_info()[1] x = sys.exc_info()[1]
@ -648,7 +648,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
if not wait_for_response: if not wait_for_response:
return None, None return None, None
return self._receive_from_client(print_debug_info=print_debug_info) return self._receive_from_client(print_debug_info=print_debug_info)
except socket.timeout: except TimeoutError:
self._debug('timeout communicating with device') self._debug('timeout communicating with device')
self._close_device_socket() self._close_device_socket()
raise TimeoutError('Device did not respond in reasonable time') raise TimeoutError('Device did not respond in reasonable time')
@ -676,7 +676,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
self._debug('receive after decode') # , v) self._debug('receive after decode') # , v)
return (self.reverse_opcodes[v[0]], v[1]) return (self.reverse_opcodes[v[0]], v[1])
self._debug('protocol error -- empty json string') self._debug('protocol error -- empty json string')
except socket.timeout: except TimeoutError:
self._debug('timeout communicating with device') self._debug('timeout communicating with device')
self._close_device_socket() self._close_device_socket()
raise TimeoutError('Device did not respond in reasonable time') raise TimeoutError('Device did not respond in reasonable time')
@ -1202,7 +1202,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
pass pass
return True return True
except socket.timeout: except TimeoutError:
self._close_device_socket() self._close_device_socket()
except OSError: except OSError:
x = sys.exc_info()[1] x = sys.exc_info()[1]

View File

@ -61,7 +61,7 @@ class PDFInput(InputFormatPlugin):
from calibre.ebooks.pdf.reflow import PDFDocument from calibre.ebooks.pdf.reflow import PDFDocument
from calibre.utils.cleantext import clean_ascii_chars from calibre.utils.cleantext import clean_ascii_chars
pdftohtml(os.getcwd(), stream.name, self.opts.no_images, as_xml=True) pdftohtml(os.getcwd(), stream.name, self.opts.no_images, as_xml=True)
with open(u'index.xml', 'rb') as f: with open('index.xml', 'rb') as f:
xml = clean_ascii_chars(f.read()) xml = clean_ascii_chars(f.read())
PDFDocument(xml, self.opts, self.log) PDFDocument(xml, self.opts, self.log)
else: else:

View File

@ -40,7 +40,7 @@ class FDST:
def __str__(self): def __str__(self):
ans = ['FDST record'] ans = ['FDST record']
def a(k, v): def a(k, v):
return ans.append('{}: {}'.format(k, v)) return ans.append(f'{k}: {v}')
a('Offset to sections', self.sec_off) a('Offset to sections', self.sec_off)
a('Number of section records', self.num_sections) a('Number of section records', self.num_sections)
ans.append('**** %d Sections ****'% len(self.sections)) ans.append('**** %d Sections ****'% len(self.sections))

View File

@ -14,7 +14,6 @@ import sys
from collections import defaultdict from collections import defaultdict
from itertools import count from itertools import count
from operator import attrgetter from operator import attrgetter
from typing import Optional
from lxml import etree, html from lxml import etree, html
@ -1027,7 +1026,7 @@ class Manifest:
# }}} # }}}
@property @property
def data_as_bytes_or_none(self) -> Optional[bytes]: def data_as_bytes_or_none(self) -> bytes | None:
if self._loader is None: if self._loader is None:
return None return None
return self._loader(getattr(self, 'html_input_href', self.href)) return self._loader(getattr(self, 'html_input_href', self.href))

View File

@ -385,7 +385,7 @@ class Text(Element):
return self.raw return self.raw
def dump(self, f): def dump(self, f):
f.write('T top={}, left={}, width={}, height={}: '.format(self.top, self.left, self.width, self.height)) f.write(f'T top={self.top}, left={self.left}, width={self.width}, height={self.height}: ')
f.write(self.to_html().encode('utf-8')) f.write(self.to_html().encode('utf-8'))
f.write('\n') f.write('\n')
@ -422,7 +422,7 @@ class Paragraph(Text):
return self.raw return self.raw
def dump(self, f): def dump(self, f):
f.write('P top={}, left={}, width={}, height={}: '.format(self.top, self.left, self.width, self.height)) f.write(f'P top={self.top}, left={self.left}, width={self.width}, height={self.height}: ')
f.write(self.to_html().encode('utf-8')) f.write(self.to_html().encode('utf-8'))
f.write('\n') f.write('\n')

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3' __license__ = 'GPL 3'
__copyright__ = '2010 Hiroshi Miura <miurahr@linux.com>' __copyright__ = '2010 Hiroshi Miura <miurahr@linux.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3' __license__ = 'GPL 3'
__copyright__ = '2010 Hiroshi Miura <miurahr@linux.com>' __copyright__ = '2010 Hiroshi Miura <miurahr@linux.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3' __license__ = 'GPL 3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>' __copyright__ = '2009, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3' __license__ = 'GPL 3'
__copyright__ = '2010 Hiroshi Miura <miurahr@linux.com>' __copyright__ = '2010 Hiroshi Miura <miurahr@linux.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL 3' __license__ = 'GPL 3'
__copyright__ = '2010 Hiroshi Miura <miurahr@linux.com>' __copyright__ = '2010 Hiroshi Miura <miurahr@linux.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'

View File

@ -106,7 +106,7 @@ class AllGUIActions(InterfaceAction):
for n,v in kbd.keys_map.items(): for n,v in kbd.keys_map.items():
act_name = kbd.shortcuts[n]['name'].lower() act_name = kbd.shortcuts[n]['name'].lower()
if act_name in lower_names: if act_name in lower_names:
shortcuts = list((sc.toString() for sc in v)) shortcuts = list(sc.toString() for sc in v)
shortcut_map[act_name] = f'\t{", ".join(shortcuts)}' shortcut_map[act_name] = f'\t{", ".join(shortcuts)}'
# This function constructs a menu action, dealing with the action being # This function constructs a menu action, dealing with the action being

View File

@ -97,12 +97,12 @@ class DocViewer(Dialog):
b = self.back_button = self.bb.addButton(_('&Back'), QDialogButtonBox.ButtonRole.ActionRole) b = self.back_button = self.bb.addButton(_('&Back'), QDialogButtonBox.ButtonRole.ActionRole)
b.clicked.connect(self.back) b.clicked.connect(self.back)
b.setToolTip((_('Displays the previously viewed function'))) b.setToolTip(_('Displays the previously viewed function'))
b.setEnabled(False) b.setEnabled(False)
b = self.bb.addButton(_('Show &all functions'), QDialogButtonBox.ButtonRole.ActionRole) b = self.bb.addButton(_('Show &all functions'), QDialogButtonBox.ButtonRole.ActionRole)
b.clicked.connect(self.show_all_functions_button_clicked) b.clicked.connect(self.show_all_functions_button_clicked)
b.setToolTip((_('Shows a list of all built-in functions in alphabetic order'))) b.setToolTip(_('Shows a list of all built-in functions in alphabetic order'))
def back(self): def back(self):
if not self.back_stack: if not self.back_stack:

View File

@ -3,7 +3,7 @@
from time import monotonic from time import monotonic
from typing import NamedTuple, Tuple from typing import NamedTuple
from qt.core import QObject, QTimer, pyqtSignal from qt.core import QObject, QTimer, pyqtSignal
@ -18,7 +18,7 @@ class ExtraFile(NamedTuple):
class ExtraFiles(NamedTuple): class ExtraFiles(NamedTuple):
last_changed_at: float last_changed_at: float
files: Tuple[ExtraFile, ...] files: tuple[ExtraFile, ...]
class ExtraFilesWatcher(QObject): class ExtraFilesWatcher(QObject):

View File

@ -3,8 +3,8 @@
import time import time
import traceback import traceback
from collections.abc import Iterator
from operator import attrgetter from operator import attrgetter
from typing import Iterator, List
from qt.core import ( from qt.core import (
QAbstractItemView, QAbstractItemView,
@ -102,7 +102,7 @@ class TrashList(QListWidget):
restore_item = pyqtSignal(object, object) restore_item = pyqtSignal(object, object)
def __init__(self, entries: List[TrashEntry], parent: 'TrashView', is_books: bool): def __init__(self, entries: list[TrashEntry], parent: 'TrashView', is_books: bool):
super().__init__(parent) super().__init__(parent)
self.is_books = is_books self.is_books = is_books
self.db = parent.db self.db = parent.db

View File

@ -8,12 +8,13 @@ import os
import re import re
import sys import sys
from collections import deque from collections import deque
from collections.abc import Iterable, Iterator
from contextlib import suppress from contextlib import suppress
from dataclasses import dataclass from dataclasses import dataclass
from functools import lru_cache from functools import lru_cache
from itertools import count from itertools import count
from time import monotonic from time import monotonic
from typing import BinaryIO, Iterable, Iterator from typing import BinaryIO
from qt.core import ( from qt.core import (
QAudio, QAudio,

View File

@ -2,7 +2,7 @@
# License: GPL v3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
from contextlib import suppress from contextlib import suppress
from typing import List, NamedTuple, Optional, Tuple from typing import NamedTuple
from css_parser.css import CSSRule from css_parser.css import CSSRule
from css_selectors import Select, SelectorError from css_selectors import Select, SelectorError
@ -21,9 +21,9 @@ class NoMatchingRuleFound(KeyError):
class RuleLocation(NamedTuple): class RuleLocation(NamedTuple):
rule_address: List[int] rule_address: list[int]
file_name: str file_name: str
style_tag_address: Optional[Tuple[int, List[int]]] = None style_tag_address: tuple[int, list[int]] | None = None
def rule_matches_elem(rule, elem, select, class_name): def rule_matches_elem(rule, elem, select, class_name):

View File

@ -13,7 +13,6 @@ import traceback
from contextlib import suppress from contextlib import suppress
from functools import lru_cache, partial from functools import lru_cache, partial
from io import BytesIO from io import BytesIO
from typing import Union
from calibre import as_unicode from calibre import as_unicode
from calibre.constants import iswindows from calibre.constants import iswindows
@ -162,7 +161,7 @@ def is_ip_trusted(remote_addr, trusted_ips):
return False return False
def is_local_address(addr: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, None]): def is_local_address(addr: ipaddress.IPv4Address | ipaddress.IPv6Address | None):
if addr is None: if addr is None:
return False return False
if addr.is_loopback: if addr.is_loopback:

View File

@ -92,7 +92,7 @@ class LoopTest(BaseTest):
with self.assertRaises(socket.timeout): with self.assertRaises(socket.timeout):
res = conn.getresponse() res = conn.getresponse()
if int(res.status) == int(http_client.REQUEST_TIMEOUT): if int(res.status) == int(http_client.REQUEST_TIMEOUT):
raise socket.timeout('Timeout') raise TimeoutError('Timeout')
raise Exception('Got unexpected response: code: {} {} headers: {!r} data: {!r}'.format( raise Exception('Got unexpected response: code: {} {} headers: {!r} data: {!r}'.format(
res.status, res.reason, res.getheaders(), res.read())) res.status, res.reason, res.getheaders(), res.read()))
self.ae(pool.busy, 1) self.ae(pool.busy, 1)

View File

@ -6,8 +6,8 @@ import shutil
import stat import stat
import time import time
from collections import defaultdict from collections import defaultdict
from collections.abc import Callable
from contextlib import suppress from contextlib import suppress
from typing import Callable, Dict, List, Set, Tuple, Union
from calibre.constants import filesystem_encoding, iswindows from calibre.constants import filesystem_encoding, iswindows
from calibre.utils.filenames import make_long_path_useable, samefile, windows_hardlink from calibre.utils.filenames import make_long_path_useable, samefile, windows_hardlink
@ -16,14 +16,14 @@ if iswindows:
from calibre_extensions import winutil from calibre_extensions import winutil
WINDOWS_SLEEP_FOR_RETRY_TIME = 2 # seconds WINDOWS_SLEEP_FOR_RETRY_TIME = 2 # seconds
WindowsFileId = Tuple[int, int, int] WindowsFileId = tuple[int, int, int]
class UnixFileCopier: class UnixFileCopier:
def __init__(self, delete_all=False, allow_move=False): def __init__(self, delete_all=False, allow_move=False):
self.delete_all = delete_all self.delete_all = delete_all
self.allow_move = allow_move self.allow_move = allow_move
self.copy_map: Dict[str, str] = {} self.copy_map: dict[str, str] = {}
def register(self, path: str, dest: str) -> None: def register(self, path: str, dest: str) -> None:
self.copy_map[path] = dest self.copy_map[path] = dest
@ -81,12 +81,12 @@ class WindowsFileCopier:
def __init__(self, delete_all=False, allow_move=False): def __init__(self, delete_all=False, allow_move=False):
self.delete_all = delete_all self.delete_all = delete_all
self.allow_move = allow_move self.allow_move = allow_move
self.path_to_fileid_map : Dict[str, WindowsFileId] = {} self.path_to_fileid_map : dict[str, WindowsFileId] = {}
self.fileid_to_paths_map: Dict[WindowsFileId, Set[str]] = defaultdict(set) self.fileid_to_paths_map: dict[WindowsFileId, set[str]] = defaultdict(set)
self.path_to_handle_map: Dict[str, 'winutil.Handle'] = {} self.path_to_handle_map: dict[str, 'winutil.Handle'] = {}
self.folder_to_handle_map: Dict[str, 'winutil.Handle'] = {} self.folder_to_handle_map: dict[str, 'winutil.Handle'] = {}
self.folders: List[str] = [] self.folders: list[str] = []
self.copy_map: Dict[str, str] = {} self.copy_map: dict[str, str] = {}
def register(self, path: str, dest: str) -> None: def register(self, path: str, dest: str) -> None:
with suppress(OSError): with suppress(OSError):
@ -184,11 +184,11 @@ class WindowsFileCopier:
winutil.move_file(make_long_path_useable(src_path), make_long_path_useable(dest_path)) winutil.move_file(make_long_path_useable(src_path), make_long_path_useable(dest_path))
def get_copier(delete_all=False, allow_move=False) -> Union[UnixFileCopier, WindowsFileCopier]: def get_copier(delete_all=False, allow_move=False) -> UnixFileCopier | WindowsFileCopier:
return (WindowsFileCopier if iswindows else UnixFileCopier)(delete_all, allow_move) return (WindowsFileCopier if iswindows else UnixFileCopier)(delete_all, allow_move)
def rename_files(src_to_dest_map: Dict[str, str]) -> None: def rename_files(src_to_dest_map: dict[str, str]) -> None:
' Rename a bunch of files. On Windows all files are locked before renaming so no other process can interfere. ' ' Rename a bunch of files. On Windows all files are locked before renaming so no other process can interfere. '
copier = get_copier(allow_move=True) copier = get_copier(allow_move=True)
for s, d in src_to_dest_map.items(): for s, d in src_to_dest_map.items():
@ -197,7 +197,7 @@ def rename_files(src_to_dest_map: Dict[str, str]) -> None:
copier.rename_all() copier.rename_all()
def copy_files(src_to_dest_map: Dict[str, str], delete_source: bool = False) -> None: def copy_files(src_to_dest_map: dict[str, str], delete_source: bool = False) -> None:
copier = get_copier(delete_source) copier = get_copier(delete_source)
for s, d in src_to_dest_map.items(): for s, d in src_to_dest_map.items():
if not samefile(s, d): if not samefile(s, d):
@ -211,7 +211,7 @@ def identity_transform(src_path: str, dest_path: str) -> str:
def register_folder_recursively( def register_folder_recursively(
src: str, copier: Union[UnixFileCopier, WindowsFileCopier], dest_dir: str, src: str, copier: UnixFileCopier | WindowsFileCopier, dest_dir: str,
transform_destination_filename: Callable[[str, str], str] = identity_transform, transform_destination_filename: Callable[[str, str], str] = identity_transform,
read_only: bool = False read_only: bool = False
) -> None: ) -> None:

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
__license__ = 'GPL v3' __license__ = 'GPL v3'

View File

@ -8,7 +8,6 @@ import os
import secrets import secrets
import stat import stat
import struct import struct
from typing import Optional, Union
from calibre.constants import ismacos, iswindows from calibre.constants import ismacos, iswindows
@ -44,7 +43,7 @@ class SharedMemory:
''' '''
_fd: int = -1 _fd: int = -1
_name: str = '' _name: str = ''
_mmap: Optional[mmap.mmap] = None _mmap: mmap.mmap | None = None
_size: int = 0 _size: int = 0
size_fmt = '!I' size_fmt = '!I'
num_bytes_for_size = struct.calcsize(size_fmt) num_bytes_for_size = struct.calcsize(size_fmt)
@ -158,7 +157,7 @@ class SharedMemory:
def flush(self) -> None: def flush(self) -> None:
self.mmap.flush() self.mmap.flush()
def write_data_with_size(self, data: Union[str, bytes]) -> None: def write_data_with_size(self, data: str | bytes) -> None:
if isinstance(data, str): if isinstance(data, str):
data = data.encode('utf-8') data = data.encode('utf-8')
sz = struct.pack(self.size_fmt, len(data)) sz = struct.pack(self.size_fmt, len(data))

View File

@ -9,9 +9,10 @@ import calendar
import json import json
import os import os
import zipfile import zipfile
from collections.abc import Sequence
from datetime import timedelta from datetime import timedelta
from threading import RLock from threading import RLock
from typing import Dict, NamedTuple, Optional, Sequence from typing import NamedTuple
from lxml import etree from lxml import etree
from lxml.builder import ElementMaker from lxml.builder import ElementMaker
@ -298,7 +299,7 @@ class RecipeCustomization(NamedTuple):
add_title_tag: bool = False add_title_tag: bool = False
custom_tags: Sequence[str] = () custom_tags: Sequence[str] = ()
keep_issues: int = 0 keep_issues: int = 0
recipe_specific_options: Optional[Dict[str, str]] = None recipe_specific_options: dict[str, str] | None = None
class SchedulerConfig: class SchedulerConfig:

View File

@ -1,6 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import absolute_import, division, print_function, unicode_literals
import json import json
from pprint import pprint from pprint import pprint