This commit is contained in:
Kovid Goyal 2024-12-15 19:07:08 +05:30
commit 5c9d3386ee
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
118 changed files with 657 additions and 594 deletions

View File

@ -23,8 +23,9 @@ from setup import __appname__, __version__
sys.path.append(base)
import calibre.utils.img as cimg
import custom
import calibre.utils.img as cimg
from calibre.utils.localization import localize_website_link
del sys.path[0]

View File

@ -299,7 +299,7 @@ def render_options(cmd, groups, options_header=True, add_program=True, header_le
def mark_options(raw):
raw = re.sub(r'(\s+)--(\s+)', u'\\1``--``\\2', raw)
raw = re.sub(r'(\s+)--(\s+)', r'\1``--``\2', raw)
def sub(m):
opt = m.group()

View File

@ -8,13 +8,13 @@ __docformat__ = 'restructuredtext en'
import os
from sphinx.builders.epub3 import Epub3Builder as EpubBuilder
from calibre.ebooks.oeb.base import OPF
from calibre.ebooks.oeb.polish.check.links import UnreferencedResource, check_links
from calibre.ebooks.oeb.polish.container import OEB_DOCS, get_container
from calibre.ebooks.oeb.polish.pretty import pretty_html_tree, pretty_opf
from calibre.utils.imghdr import identify
from sphinx.builders.epub3 import Epub3Builder as EpubBuilder
from polyglot.builtins import iteritems

View File

@ -7,14 +7,15 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
import re
from css_parser.css import CSSRule
from qt.core import QAction, QInputDialog
from calibre import force_unicode
from calibre.ebooks.oeb.polish.container import OEB_DOCS, OEB_STYLES, serialize
from calibre.gui2 import error_dialog
# The base class that all tools must inherit from
from calibre.gui2.tweak_book.plugin import Tool
from css_parser.css import CSSRule
from qt.core import QAction, QInputDialog
class DemoTool(Tool):

View File

@ -6,9 +6,10 @@ __license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.utils.config import JSONConfig
from qt.core import QHBoxLayout, QLabel, QLineEdit, QWidget
from calibre.utils.config import JSONConfig
# This is where all preferences for this plugin will be stored
# Remember that this name (i.e. plugins/interface_demo) is also
# in a global namespace, so make it as unique as possible.

View File

@ -12,9 +12,10 @@ if False:
# You do not need this code in your plugins
get_icons = get_resources = None
from calibre_plugins.interface_demo.config import prefs
from qt.core import QDialog, QLabel, QMessageBox, QPushButton, QVBoxLayout
from calibre_plugins.interface_demo.config import prefs
class DemoDialog(QDialog):

View File

@ -4,24 +4,35 @@ requires-python = ">=3.8"
[tool.ruff]
line-length = 160
target-version = 'py38'
builtins = ['_']
[tool.ruff.lint]
ignore = ['E741', 'E402', 'E722', 'E401']
select = ['E', 'F', 'I']
[tool.ruff.lint.per-file-ignores]
"src/calibre/ebooks/unihandecode/unicodepoints.py" = ["E501"]
"src/qt/__init__.py" = ["E501"]
builtins = ['_', 'I', 'P']
include = ['*.py', '*.recipe']
exclude = [
"*_ui.py",
"bypy/*",
"setup/*",
"src/css_selectors/*",
"src/polyglot/*",
"src/templite/*",
"src/tinycss/*",
]
[tool.ruff.format]
quote-style = 'single'
[tool.ruff.lint]
ignore = ['E402', 'E722', 'E741', 'E401']
select = ['E', 'F', 'I', 'W']
[tool.ruff.lint.per-file-ignores]
"src/calibre/ebooks/unihandecode/*codepoints.py" = ['E501', 'W191']
"src/qt/*.py" = ['I']
"src/qt/*.pyi" = ['I']
[tool.ruff.lint.isort]
detect-same-package = true
extra-standard-library = ['aes', 'elementmaker', 'encodings']
known-first-party = ["calibre_extensions", 'polyglot']
known-third-party = ["qt"]
extra-standard-library = ["aes", "elementmaker", "encodings"]
known-first-party = ["calibre_extensions", "calibre_plugins", "polyglot"]
known-third-party = ["odf", "qt", "templite", "tinycss", "css_selectors"]
relative-imports-order = "closest-to-furthest"
split-on-trailing-comma = false
section-order = ['__python__', "future", "standard-library", "third-party", "first-party", "local-folder"]

View File

@ -7,12 +7,13 @@ from collections import defaultdict
from datetime import datetime, timedelta
from urllib.parse import quote, urlencode
from html5_parser import parse
from lxml import etree
from calibre import replace_entities
from calibre.ebooks.BeautifulSoup import NavigableString, Tag
from calibre.ptempfile import PersistentTemporaryFile
from calibre.web.feeds.news import BasicNewsRecipe
from html5_parser import parse
from lxml import etree
use_archive = True

View File

@ -4,9 +4,10 @@ __copyright__ = '2010, Walt Anthony <workshop.northpole at gmail.com>'
www.americanthinker.com
'''
import html5lib
from lxml import etree
from calibre.utils.cleantext import clean_xml_chars
from calibre.web.feeds.news import BasicNewsRecipe
from lxml import etree
class AmericanThinker(BasicNewsRecipe):

View File

@ -4,9 +4,10 @@ __copyright__ = '2009-2010, Darko Miletic <darko.miletic at gmail.com>'
spectator.org
'''
from calibre.web.feeds.news import BasicNewsRecipe
from css_selectors import Select
from calibre.web.feeds.news import BasicNewsRecipe
class TheAmericanSpectator(BasicNewsRecipe):
title = 'The American Spectator'

View File

@ -3,9 +3,10 @@
import json
from datetime import datetime
from calibre.web.feeds.news import BasicNewsRecipe
from html5_parser import parse
from calibre.web.feeds.news import BasicNewsRecipe
class BusinessStandard(BasicNewsRecipe):
title = 'Business Standard'

View File

@ -3,9 +3,10 @@
import json
from urllib.parse import quote, urlparse
from calibre.web.feeds.news import BasicNewsRecipe
from mechanize import Request
from calibre.web.feeds.news import BasicNewsRecipe
def absurl(x):
if x.startswith('//'):

View File

@ -18,7 +18,6 @@ class herald(BasicNewsRecipe):
keep_only_tags = [
classes('article-title article-author__name'),
dict(name='div', attrs={'id':'main-content'})
]
remove_tags = [

View File

@ -16,6 +16,7 @@ try:
except ImportError:
from cookielib import Cookie
import mechanize
from calibre.web.feeds.news import BasicNewsRecipe

View File

@ -5,9 +5,10 @@ www.esquire.com
'''
from collections import defaultdict
from calibre.web.feeds.news import BasicNewsRecipe
from css_selectors import Select
from calibre.web.feeds.news import BasicNewsRecipe
def absolutize(url):
if url.startswith('/'):

View File

@ -4,9 +4,10 @@ import json
import re
from urllib.parse import quote
from html5_parser import parse
from calibre import browser
from calibre.web.feeds.news import BasicNewsRecipe, classes
from html5_parser import parse
class ft(BasicNewsRecipe):

View File

@ -7,9 +7,10 @@ __copyright__ = '2017, John Hutson <jfhutson at gmail.com>'
firstthings.com
'''
import html5lib
from calibre.web.feeds.news import BasicNewsRecipe
from lxml import html
from calibre.web.feeds.news import BasicNewsRecipe
class FirstThings(BasicNewsRecipe):

View File

@ -4,9 +4,10 @@ import re
import html5lib
import mechanize
from calibre.web.feeds.news import BasicNewsRecipe, classes
from lxml import html
from calibre.web.feeds.news import BasicNewsRecipe, classes
def as_article(source, log):
url = source['url']

View File

@ -8,9 +8,10 @@ __copyright__ = '2011, Piotr Kontek, piotr.kontek@gmail.com \
import re
import time
from calibre.web.feeds.news import BasicNewsRecipe
from lxml import html
from calibre.web.feeds.news import BasicNewsRecipe
class GN(BasicNewsRecipe):
__author__ = 'Piotr Kontek, Tomasz Długosz'

View File

@ -7,9 +7,10 @@ __copyright__ = '2011, Piotr Kontek, piotr.kontek@gmail.com \
import re
from calibre.web.feeds.news import BasicNewsRecipe
from lxml import html
from calibre.web.feeds.news import BasicNewsRecipe
class GN(BasicNewsRecipe):
__author__ = 'Piotr Kontek, Tomasz Długosz'

View File

@ -5,9 +5,10 @@ import re
from collections import OrderedDict
from urllib.parse import urlencode, urljoin
from mechanize import Request
from calibre import browser, random_user_agent
from calibre.web.feeds.news import BasicNewsRecipe, classes
from mechanize import Request
class HBR(BasicNewsRecipe):

View File

@ -2,9 +2,10 @@
# vim:fileencoding=utf-8
import json
from calibre.web.feeds.news import BasicNewsRecipe
from html5_parser import parse
from calibre.web.feeds.news import BasicNewsRecipe
def get_story(story):
str_type = story.get('type', '')

View File

@ -8,9 +8,10 @@ import json
from contextlib import closing
from time import sleep
from calibre.web.feeds.news import BasicNewsRecipe
from mechanize import Request
from calibre.web.feeds.news import BasicNewsRecipe
def absolutize(url):
if url.startswith('/'):

View File

@ -118,9 +118,10 @@ class Mediapart(BasicNewsRecipe):
'''
Create a generic cover for recipes that don't have a cover
'''
from calibre.gui2 import ensure_app, load_builtin_fonts, pixmap_to_data
from qt.core import QFont, QImage, QPainter, QPen, QRect, Qt
from calibre.gui2 import ensure_app, load_builtin_fonts, pixmap_to_data
def init_environment():
ensure_app()
load_builtin_fonts()

View File

@ -4,9 +4,10 @@ import json
import re
from contextlib import closing
from calibre.web.feeds.recipes import BasicNewsRecipe
from mechanize import Request
from calibre.web.feeds.recipes import BasicNewsRecipe
class NRC(BasicNewsRecipe):
title = 'NRC'

View File

@ -1,9 +1,10 @@
import json
from datetime import datetime
from calibre.web.feeds.recipes import BasicNewsRecipe
from mechanize import Request
from calibre.web.feeds.recipes import BasicNewsRecipe
class Nzz(BasicNewsRecipe):
title = 'NZZ'

View File

@ -7,9 +7,10 @@ odb.org
import uuid
from calibre.web.feeds.news import BasicNewsRecipe
from lxml import html
from calibre.web.feeds.news import BasicNewsRecipe
class OurDailyBread(BasicNewsRecipe):
title = 'Our Daily Bread'

View File

@ -3,9 +3,10 @@ import json
import uuid
from contextlib import closing
from calibre.web.feeds.recipes import BasicNewsRecipe
from mechanize import Request
from calibre.web.feeds.recipes import BasicNewsRecipe
class Parool(BasicNewsRecipe):
title = 'Het Parool'

View File

@ -9,7 +9,6 @@ publico.pt
'''
from calibre.web.feeds.news import BasicNewsRecipe
from polyglot.urllib import urlencode

View File

@ -10,9 +10,10 @@ import re
# This imports the version bundled with Calibre
import lxml
from lxml.builder import E
from calibre.ebooks.BeautifulSoup import BeautifulSoup
from calibre.web.feeds.recipes import BasicNewsRecipe
from lxml.builder import E
respekt_url = 'https://www.respekt.cz'

View File

@ -15,10 +15,11 @@ except ImportError:
from urllib import urlencode
import re
from mechanize import Request
from calibre import strftime
from calibre.ptempfile import PersistentTemporaryFile
from calibre.web.feeds.news import BasicNewsRecipe
from mechanize import Request
class TheNewCriterion(BasicNewsRecipe):

View File

@ -69,7 +69,7 @@ class PrivateEyeRecipe(BasicNewsRecipe):
article['author'] = author
return article
edition_re = re.compile('(?:-front-cover-)(\d+)-')
edition_re = re.compile(r'(?:-front-cover-)(\d+)-')
# Identify the cover image and extract the edition# from the url
def get_cover_url(self):
@ -242,8 +242,8 @@ class PrivateEyeRecipe(BasicNewsRecipe):
{'name': 'a', 'class': "view-full-screen"},
{'name': 'div', 'class': "image-counter"},
{'name': 'h2', 'string': "Find out more about The Oldie"},
{'name': 'a', 'href': re.compile("^https?:\/\/issuu.com\/")},
{'name': 'img', 'src': re.compile("\/assets\/images\/icons\/icon-")},
{'name': 'a', 'href': re.compile(r"^https?:\/\/issuu.com\/")},
{'name': 'img', 'src': re.compile(r"\/assets\/images\/icons\/icon-")},
]
# The following extra css is to tweak the formatting of various elements of various article pages.

View File

@ -16,7 +16,7 @@ def re_html(y):
def get_id(url):
rq = browser().open(url)
return re.search('\?p=(\S+)>', str(rq.info())).group(1)
return re.search(r'\?p=(\S+)>', str(rq.info())).group(1)
class TLS(BasicNewsRecipe):

View File

@ -34,4 +34,3 @@ class Ugeskriftet(BasicNewsRecipe):
feeds = [
('Ugeskriftet for læger', 'https://ugeskriftet.dk/rss/forside'),
]

View File

@ -88,7 +88,6 @@ class USAToday(BasicNewsRecipe):
feeds.append((section, articles))
return feeds
def preprocess_html(self, soup):
for img in soup.findAll('img', src=True):
img['src'] = 'https://www.usatoday.com' + img['src']

View File

@ -3,9 +3,10 @@ import json
import uuid
from contextlib import closing
from calibre.web.feeds.recipes import BasicNewsRecipe
from mechanize import Request
from calibre.web.feeds.recipes import BasicNewsRecipe
class Volkskrant(BasicNewsRecipe):
title = 'Volkskrant'

View File

@ -10,11 +10,12 @@ import time
from base64 import standard_b64encode
from datetime import date, timedelta
from calibre.ptempfile import PersistentTemporaryFile
from calibre.web.feeds.news import BasicNewsRecipe
from css_selectors import Select
from mechanize import Request
from calibre.ptempfile import PersistentTemporaryFile
from calibre.web.feeds.news import BasicNewsRecipe
try:
import urllib.parse as urlparse
except ImportError:

View File

@ -223,7 +223,7 @@ per_language_title_sort_articles = {
'spa': (r'El\s+', r'La\s+', r'Lo\s+', r'Los\s+', r'Las\s+', r'Un\s+',
r'Una\s+', r'Unos\s+', r'Unas\s+'),
# French
'fra' : (r'Le\s+', r'La\s+', r"L'", u'L´', u'L', r'Les\s+', r'Un\s+', r'Une\s+',
'fra': (r'Le\s+', r'La\s+', r"L'", r'L´', r'L', r'Les\s+', r'Un\s+', r'Une\s+',
r'Des\s+', r'De\s+La\s+', r'De\s+', r"D'", r'D´', r'D'),
# Polish
'pol': (),

View File

@ -41,6 +41,10 @@ class Check(Command):
CACHE = 'check.json'
def add_options(self, parser):
parser.add_option('--fix', '--auto-fix', default=False, action='store_true',
help='Try to automatically fix some of the smallest errors')
def get_files(self):
yield from checkable_python_files(self.SRC)
@ -91,6 +95,10 @@ class Check(Command):
p = subprocess.Popen(['python', self.j(self.wn_path, 'whats_new.py'), f])
return p.wait() != 0
def perform_auto_fix(self):
p = subprocess.Popen(['ruff', 'check', '--fix-only'], text=True, stdout=subprocess.PIPE)
return p.stdout.read()
def run(self, opts):
self.fhash_cache = {}
cache = {}
@ -102,13 +110,20 @@ class Check(Command):
except OSError as err:
if err.errno != errno.ENOENT:
raise
if opts.fix:
self.info('\tAuto-fixing')
msg = self.perform_auto_fix()
self.info(msg)
dirty_files = tuple(f for f in self.get_files() if not self.is_cache_valid(f, cache))
try:
for i, f in enumerate(dirty_files):
self.info('\tChecking', f)
if self.file_has_errors(f):
self.info('%d files left to check' % (len(dirty_files) - i - 1))
try:
edit_file(f)
except FileNotFoundError:
pass # continue if the configured editor fail to be open
if self.file_has_errors(f):
raise SystemExit(1)
cache[f] = self.file_hash(f)

View File

@ -11,7 +11,7 @@ class PageGroup:
"""Simulate constructor overloading"""
def __init__(self, page_locations: Union[int, List[int]], page_number_type: PageNumberTypes, first_value: int,
page_labels: Union[str, List[str], None] = None):
if page_locations.__class__ == int:
if page_locations.__class__ is int:
self.page_locations: List[int] = [page_locations]
else:
self.page_locations: List[int] = page_locations
@ -19,7 +19,7 @@ class PageGroup:
self.__first_value = first_value
if page_number_type == PageNumberTypes.Custom:
assert page_labels is not None
if page_labels.__class__ == str:
if page_labels.__class__ is str:
assert 1 == len(self.page_locations) and len(page_labels) > 0
self.__page_number_labels: List[str] = [page_labels]
else:
@ -28,7 +28,7 @@ class PageGroup:
self.__page_number_labels: List[str] = page_labels
def append(self, page_location: Union[int, Tuple[int, str]]) -> None:
if page_location.__class__ == int:
if page_location.__class__ is int:
assert self.__page_number_type != PageNumberTypes.Custom
self.page_locations.append(page_location)
else:

View File

@ -11,7 +11,7 @@ from calibre.devices.kindle.apnx_page_generator.page_number_type import PageNumb
class Pages:
def __init__(self, page_locations: Optional[List[int]] = None):
if page_locations.__class__ == list:
if page_locations.__class__ is list:
self.__pages_groups: List[PageGroup] = [PageGroup(page_locations, PageNumberTypes.Arabic, 1)]
else:
self.__pages_groups: List[PageGroup] = []

View File

@ -673,7 +673,7 @@ class Device(DeviceConfig, DevicePlugin):
hal = get_hal()
vols = hal.get_volumes(d)
if verbose:
print("FBSD: ", vols)
print("FBSD:\t", vols)
ok, mv = hal.mount_volumes(vols)
if not ok:

View File

@ -106,19 +106,19 @@ class HAL:
# Mount Point becomes Mount Path
mp += '/'
if DEBUG:
print("FBSD: mounted", vol['label'], "on", mp)
print("FBSD:\tmounted", vol['label'], "on", mp)
if mtd == 0:
ans['_main_prefix'], ans['_main_vol'] = mp, vol['vol']
if DEBUG:
print("FBSD: main = ", mp)
print("FBSD:\tmain = ", mp)
elif mtd == 1:
ans['_card_a_prefix'], ans['_card_a_vol'] = mp, vol['vol']
if DEBUG:
print("FBSD: card a = ", mp)
print("FBSD:\tcard a = ", mp)
elif mtd == 2:
ans['_card_b_prefix'], ans['_card_b_vol'] = mp, vol['vol']
if DEBUG:
print("FBSD: card b = ", mp)
print("FBSD:\tcard b = ", mp)
break
mtd += 1

View File

@ -5,13 +5,13 @@
import re
from functools import partial
from css_selectors.select import Select, get_parsed_selector
from html5_parser import parse
from lxml import etree
from calibre.ebooks.metadata.tag_mapper import uniq
from calibre.ebooks.oeb.base import OEB_DOCS, XPath
from calibre.ebooks.oeb.parse_utils import XHTML
from css_selectors.select import Select, get_parsed_selector
def non_empty_validator(label, val):

View File

@ -11,6 +11,8 @@ import types
from collections import defaultdict, namedtuple
from itertools import chain
from css_selectors import Select, SelectorError
from calibre import force_unicode, prepare_string_for_xml
from calibre.ebooks.oeb.base import XPath, xml2text
from calibre.ebooks.oeb.polish.container import OEB_DOCS, OEB_STYLES
@ -18,7 +20,6 @@ from calibre.ebooks.oeb.polish.spell import count_all_chars, get_all_words
from calibre.ebooks.oeb.polish.utils import OEB_FONTS
from calibre.utils.icu import numeric_sort_key, safe_chr
from calibre.utils.imghdr import identify
from css_selectors import Select, SelectorError
from polyglot.builtins import iteritems
File = namedtuple('File', 'name dir basename size category word_count')

View File

@ -5,11 +5,11 @@ from contextlib import suppress
from typing import List, NamedTuple, Optional, Tuple
from css_parser.css import CSSRule
from css_selectors import Select, SelectorError
from calibre.ebooks.oeb.parse_utils import barename
from calibre.ebooks.oeb.polish.container import get_container
from calibre.ebooks.oeb.polish.parsing import parse
from css_selectors import Select, SelectorError
class NoMatchingTagFound(KeyError):

View File

@ -1,5 +1,19 @@
# autogenerated by __main__.py do not edit
top_level_module_names=('QtCore', 'QtGui', 'QtWidgets', 'QtNetwork', 'QtSvg', 'QtPrintSupport', 'QtOpenGL', 'QtOpenGLWidgets', 'QtQuick', 'QtMultimedia', 'QtMultimediaWidgets', 'QtTextToSpeech', 'QtWebEngineCore', 'QtWebEngineWidgets', 'QtDBus')
top_level_module_names = ('QtCore',
'QtGui',
'QtWidgets',
'QtNetwork',
'QtSvg',
'QtPrintSupport',
'QtOpenGL',
'QtOpenGLWidgets',
'QtQuick',
'QtMultimedia',
'QtMultimediaWidgets',
'QtTextToSpeech',
'QtWebEngineCore',
'QtWebEngineWidgets',
'QtDBus')
def __getattr__(name):
@ -7,4 +21,3 @@ def __getattr__(name):
import importlib
return importlib.import_module("PyQt6." + name)
raise AttributeError(name)

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
import importlib
@ -56,10 +55,10 @@ qt_modules = {}
def __getattr__(name):
return dynamic_load(name, name_map, already_imported, qt_modules, module_names)
''', file=f)
''', end='', file=f)
with open(f'{base}/{name}.pyi', 'w') as f:
print('# autogenerated by __main__.py do not edit', file=f)
f.write('\n'.join(types))
print('\n'.join(types), file=f)
if name == 'core':
module_names += ('sip',)
mod = importlib.import_module(f'{QT_WRAPPER}.sip')
@ -79,7 +78,8 @@ for name in module_lists.keys():
scan(name)
with open(f'{base}/__init__.py', 'w') as f:
print('# autogenerated by __main__.py do not edit', file=f)
print(f'{top_level_module_names=}', file=f)
print('top_level_module_names = ', end='', file=f)
pprint(top_level_module_names, stream=f)
print(f'''
def __getattr__(name):
@ -87,4 +87,4 @@ def __getattr__(name):
import importlib
return importlib.import_module("{QT_WRAPPER}." + name)
raise AttributeError(name)
''', file=f)
''', end='', file=f)

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
import sys