Fixes for python 3.14 (#17)
* Replace pkg_resources with importlib.resources for Python 3.14 compatibility * fix self.XPATH() calls to OPF.XPath() instead since 3.14 made functools.partial a descriptor and thus injected self as the first argument * updated install instruction to include lxml to avoid using non matching system version * Fix regex in LRF output that stripped CThumbnail metadata. .*? matched until >\n and cut off all children on the same line --------- Co-authored-by: wave1martian <wave1martian@buildsleeprepeat.com>
This commit is contained in:
+6
-1
@@ -123,7 +123,12 @@ managers), i.e:
|
||||
$ . venv/bin/activate
|
||||
(venv) $ git clone https://github.com/gryf/ebook-converter
|
||||
(venv) $ cd ebook-converter
|
||||
(venv) $ pip install .
|
||||
(venv) $ pip install --no-binary lxml .
|
||||
|
||||
Note: the ``--no-binary lxml`` flag is required to ensure ``lxml`` is compiled
|
||||
against the same system ``libxml2`` as ``html5-parser``. Without it, pip may
|
||||
install a pre-built ``lxml`` wheel bundling a different ``libxml2`` version,
|
||||
which causes a ``RuntimeError``.
|
||||
|
||||
Simple as that. And from now on, you can issue converter:
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import mimetypes
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
|
||||
|
||||
mimetypes.init([pkg_resources.
|
||||
resource_filename('ebook_converter', 'data/mime.types')])
|
||||
mimetypes.init([str(files('ebook_converter').joinpath('data/mime.types'))])
|
||||
|
||||
@@ -7,7 +7,7 @@ import mimetypes
|
||||
import numbers
|
||||
import optparse
|
||||
import os
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
import re
|
||||
import sys
|
||||
|
||||
@@ -351,8 +351,7 @@ def read_sr_patterns(path, log=None):
|
||||
|
||||
def main(args=sys.argv):
|
||||
log = logging.default_log
|
||||
mimetypes.init([pkg_resources.resource_filename('ebook_converter',
|
||||
'data/mime.types')])
|
||||
mimetypes.init([str(files('ebook_converter').joinpath('data/mime.types'))])
|
||||
parser, plumber = create_option_parser(args, log)
|
||||
opts, leftover_args = parser.parse_args(args)
|
||||
if len(leftover_args) > 3:
|
||||
|
||||
@@ -3,7 +3,7 @@ Convert .fb2 files to .lrf
|
||||
"""
|
||||
import mimetypes
|
||||
import os
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
import re
|
||||
|
||||
from lxml import etree
|
||||
@@ -85,8 +85,8 @@ class FB2Input(InputFormatPlugin):
|
||||
css = re.sub(r'name\s*=\s*', 'class=', css)
|
||||
self.extract_embedded_content(doc)
|
||||
log.debug('Converting XML to HTML...')
|
||||
with open(pkg_resources.resource_filename('ebook_converter',
|
||||
'data/fb2.xsl')) as f:
|
||||
with open(str(files('ebook_converter')
|
||||
.joinpath('data/fb2.xsl'))) as f:
|
||||
ss = f.read()
|
||||
ss = ss.replace("__FB_NS__", fb_ns)
|
||||
if options.no_inline_fb2_toc:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import os
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
import re
|
||||
import shutil
|
||||
|
||||
@@ -92,9 +92,8 @@ class HTMLOutput(OutputFormatPlugin):
|
||||
with open(opts.template_html_index, 'rb') as f:
|
||||
template_html_index_data = f.read()
|
||||
else:
|
||||
with open(pkg_resources.
|
||||
resource_filename('ebook_converter',
|
||||
'data/html_export_default_index.tmpl')
|
||||
with open(str(files('ebook_converter')
|
||||
.joinpath('data/html_export_default_index.tmpl'))
|
||||
) as fobj:
|
||||
template_html_index_data = fobj.read().decode()
|
||||
|
||||
@@ -102,9 +101,8 @@ class HTMLOutput(OutputFormatPlugin):
|
||||
with open(opts.template_html, 'rb') as f:
|
||||
template_html_data = f.read()
|
||||
else:
|
||||
with open(pkg_resources.
|
||||
resource_filename('ebook_converter',
|
||||
'data/html_export_default.tmpl')
|
||||
with open(str(files('ebook_converter')
|
||||
.joinpath('data/html_export_default.tmpl'))
|
||||
) as fobj:
|
||||
template_html_data = fobj.read().decode()
|
||||
|
||||
@@ -112,9 +110,8 @@ class HTMLOutput(OutputFormatPlugin):
|
||||
with open(opts.template_css, 'rb') as f:
|
||||
template_css_data = f.read()
|
||||
else:
|
||||
with open(pkg_resources.
|
||||
resource_filename('ebook_converter',
|
||||
'data/html_export_default.css')
|
||||
with open(str(files('ebook_converter')
|
||||
.joinpath('data/html_export_default.css'))
|
||||
) as fobj:
|
||||
template_css_data = fobj.read().decode()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
import sys
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
|
||||
from lxml import etree
|
||||
|
||||
@@ -52,9 +52,8 @@ class LRFInput(InputFormatPlugin):
|
||||
|
||||
self.log.info('Converting XML to HTML...')
|
||||
|
||||
with open(pkg_resources.
|
||||
resource_filename('ebook_converter',
|
||||
'data/lrf.xsl')) as fobj:
|
||||
with open(str(files('ebook_converter')
|
||||
.joinpath('data/lrf.xsl'))) as fobj:
|
||||
# TODO(gryf): change this nonsense to etree.parse() instead.
|
||||
styledoc = etree.fromstring(fobj.read())
|
||||
media_type = MediaType()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import glob
|
||||
import os
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
@@ -288,8 +288,8 @@ class RTFInput(InputFormatPlugin):
|
||||
|
||||
self.log.info('Converting XML to HTML...')
|
||||
inline_class = InlineClass(self.log)
|
||||
with open(pkg_resources.resource_filename('ebook_converter',
|
||||
'data/rtf.xsl')) as fobj:
|
||||
with open(str(files('ebook_converter')
|
||||
.joinpath('data/rtf.xsl'))) as fobj:
|
||||
styledoc = etree.fromstring(fobj.read())
|
||||
extensions = {('calibre', 'inline-class'): inline_class}
|
||||
transform = etree.XSLT(styledoc, extensions=extensions)
|
||||
|
||||
@@ -670,7 +670,7 @@ class Info(Delegator):
|
||||
f = io.BytesIO()
|
||||
tree.write(f, encoding='utf-8', xml_declaration=True)
|
||||
xmlInfo = f.getvalue().decode('utf-8')
|
||||
xmlInfo = re.sub(r"<CThumbnail.*?>\n", "", xmlInfo)
|
||||
xmlInfo = re.sub(r"<CThumbnail[^>]*/>\s*", "", xmlInfo)
|
||||
xmlInfo = xmlInfo.replace("SumPage>", "Page>")
|
||||
lrfWriter.docInfoXml = xmlInfo
|
||||
|
||||
|
||||
@@ -697,7 +697,7 @@ class OPF(object): # {{{
|
||||
def find_toc(self):
|
||||
self.toc = None
|
||||
try:
|
||||
spine = self.XPath('descendant::*[re:match(name(), "spine", '
|
||||
spine = OPF.XPath('descendant::*[re:match(name(), "spine", '
|
||||
'"i")]')(self.root)
|
||||
toc = None
|
||||
if spine:
|
||||
@@ -801,7 +801,7 @@ class OPF(object): # {{{
|
||||
|
||||
def replace_spine_items_by_idref(self, idref, new_idrefs):
|
||||
items = list(map(self.create_spine_item, new_idrefs))
|
||||
spine = self.XPath('/opf:package/*[re:match(name(), "spine", '
|
||||
spine = OPF.XPath('/opf:package/*[re:match(name(), "spine", '
|
||||
'"i")]')(self.root)[0]
|
||||
old = [i for i in self.iterspine() if i.get('idref', None) == idref]
|
||||
for x in old:
|
||||
@@ -1005,7 +1005,7 @@ class OPF(object): # {{{
|
||||
|
||||
def get_identifiers(self):
|
||||
identifiers = {}
|
||||
for x in self.XPath(
|
||||
for x in OPF.XPath(
|
||||
'descendant::*[local-name() = "identifier" and text()]')(
|
||||
self.metadata):
|
||||
found_scheme = False
|
||||
@@ -1039,7 +1039,7 @@ class OPF(object): # {{{
|
||||
uuid_id = self.root.attrib[attr]
|
||||
break
|
||||
|
||||
for x in self.XPath(
|
||||
for x in OPF.XPath(
|
||||
'descendant::*[local-name() = "identifier"]')(
|
||||
self.metadata):
|
||||
xid = x.get('id', None)
|
||||
@@ -1175,7 +1175,7 @@ class OPF(object): # {{{
|
||||
|
||||
@property
|
||||
def page_progression_direction(self):
|
||||
spine = self.XPath('descendant::*[re:match(name(), "spine", '
|
||||
spine = OPF.XPath('descendant::*[re:match(name(), "spine", '
|
||||
'"i")][1]')(self.root)
|
||||
if spine:
|
||||
for k, v in spine[0].attrib.items():
|
||||
@@ -1185,7 +1185,7 @@ class OPF(object): # {{{
|
||||
|
||||
@property
|
||||
def primary_writing_mode(self):
|
||||
for m in self.XPath('//*[local-name()="meta" and @name="primary-'
|
||||
for m in OPF.XPath('//*[local-name()="meta" and @name="primary-'
|
||||
'writing-mode" and @content]')(self.root):
|
||||
return m.get('content')
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import collections
|
||||
import functools
|
||||
import operator
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
import re
|
||||
import urllib.parse
|
||||
|
||||
@@ -719,9 +719,8 @@ def commit_nav_toc(container, toc, lang=None, landmarks=None,
|
||||
if previous_nav is not None:
|
||||
root = previous_nav[1]
|
||||
else:
|
||||
with open(pkg_resources.
|
||||
resource_filename('ebook_converter',
|
||||
'data/new_nav.html')) as fobj:
|
||||
with open(str(files('ebook_converter')
|
||||
.joinpath('data/new_nav.html'))) as fobj:
|
||||
root = container.parse_xhtml(fobj.read())
|
||||
container.replace(tocname, root)
|
||||
else:
|
||||
@@ -874,8 +873,7 @@ def toc_to_html(toc, container, toc_name, title, lang=None):
|
||||
|
||||
E = ElementMaker(namespace=const.XHTML_NS, nsmap={None: const.XHTML_NS})
|
||||
# TODO(gryf): revisit lack of css.
|
||||
css_f = pkg_resources.resource_filename('ebook_converter',
|
||||
'data/inline_toc_styles.css')
|
||||
css_f = str(files('ebook_converter').joinpath('data/inline_toc_styles.css'))
|
||||
html = E.html(E.head(E.title(title),
|
||||
E.style(css_f, type='text/css')),
|
||||
E.body(E.h2(title), E.ul(),
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
CSS property propagation class.
|
||||
"""
|
||||
import os, re, logging, copy, unicodedata, numbers
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
from operator import itemgetter
|
||||
from weakref import WeakKeyDictionary
|
||||
from xml.dom import SyntaxErr as CSSSyntaxError
|
||||
@@ -28,8 +28,7 @@ _html_css_stylesheet = None
|
||||
def html_css_stylesheet():
|
||||
global _html_css_stylesheet
|
||||
if _html_css_stylesheet is None:
|
||||
with open(pkg_resources.resource_filename('ebook_converter',
|
||||
'data/html.css'), 'rb') as f:
|
||||
with open(str(files('ebook_converter').joinpath('data/html.css')), 'rb') as f:
|
||||
html_css = f.read().decode('utf-8')
|
||||
_html_css_stylesheet = parseString(html_css, validate=False)
|
||||
return _html_css_stylesheet
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from collections import namedtuple
|
||||
import json
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
|
||||
from ebook_converter.utils.localization import canonicalize_lang
|
||||
|
||||
@@ -16,8 +16,7 @@ ccodes, ccodemap, country_names = None, None, None
|
||||
def get_codes():
|
||||
global ccodes, ccodemap, country_names
|
||||
if ccodes is None:
|
||||
src = pkg_resources.resource_filename('ebook_converter',
|
||||
'data/iso_3166-1.json')
|
||||
src = str(files('ebook_converter').joinpath('data/iso_3166-1.json'))
|
||||
with open(src, 'rb') as f:
|
||||
db = json.load(f)
|
||||
codes = set()
|
||||
|
||||
@@ -10,7 +10,7 @@ import pickle
|
||||
import re
|
||||
import traceback
|
||||
|
||||
import pkg_resources
|
||||
from importlib.resources import files as _resource_files
|
||||
|
||||
from ebook_converter.constants_old import config_dir
|
||||
from ebook_converter.constants_old import filesystem_encoding
|
||||
@@ -591,8 +591,7 @@ def exec_tweaks(path):
|
||||
|
||||
|
||||
def default_tweaks_raw():
|
||||
return pkg_resources.resource_filename('ebook_converter',
|
||||
'data/default_tweaks.py')
|
||||
return str(_resource_files('ebook_converter').joinpath('data/default_tweaks.py'))
|
||||
|
||||
|
||||
def read_tweaks():
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import json
|
||||
import pkg_resources
|
||||
from importlib.resources import files
|
||||
|
||||
|
||||
def get_lang():
|
||||
@@ -39,8 +39,7 @@ def _load_iso639():
|
||||
# excerpt form Calibre transform code which is executed during Calibre
|
||||
# build).
|
||||
if _iso639 is None:
|
||||
src = pkg_resources.resource_filename('ebook_converter',
|
||||
'data/iso_639-3.json')
|
||||
src = str(files('ebook_converter').joinpath('data/iso_639-3.json'))
|
||||
|
||||
with open(src, 'rb') as f:
|
||||
root = json.load(f)
|
||||
|
||||
Reference in New Issue
Block a user