Allow importing calibre extension modules using import statements

from calibre_extensions import speedup
This commit is contained in:
Kovid Goyal 2020-10-15 18:01:38 +05:30
parent cc665e1ce5
commit 6690187e0a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 104 additions and 65 deletions

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
from polyglot.builtins import map, unicode_type, environ_item, hasenv, getenv, as_unicode, native_string_type
from polyglot.builtins import map, unicode_type, environ_item, hasenv, getenv
import sys, locale, codecs, os, importlib, collections
__appname__ = 'calibre'
@ -177,76 +177,32 @@ plugins_loc = sys.extensions_location
class Plugins(collections.Mapping):
def __init__(self):
self._plugins = {}
plugins = [
'pictureflow',
'lzx',
'msdes',
'podofo',
'cPalmdoc',
'progress_indicator',
'icu',
'speedup',
'html_as_json',
'unicode_names',
'html_syntax_highlighter',
'hyphen',
'freetype',
'imageops',
'hunspell',
'_patiencediff_c',
'bzzdec',
'matcher',
'tokenizer',
'certgen',
]
if iswindows:
plugins.extend(['winutil', 'wpd', 'winfonts'])
if ismacos:
plugins.append('usbobserver')
plugins.append('cocoa')
if isfreebsd or ishaiku or islinux or ismacos:
plugins.append('libusb')
plugins.append('libmtp')
self.plugins = frozenset(plugins)
def load_plugin(self, name):
if name in self._plugins:
return
if not isfrozen:
sys.path.insert(0, plugins_loc)
try:
del sys.modules[name]
except KeyError:
pass
plugin_err = ''
try:
p = importlib.import_module(name)
except Exception as err:
p = None
try:
plugin_err = unicode_type(err)
except Exception:
plugin_err = as_unicode(native_string_type(err), encoding=preferred_encoding, errors='replace')
self._plugins[name] = p, plugin_err
if not isfrozen:
sys.path.remove(plugins_loc)
def __iter__(self):
return iter(self.plugins)
from importlib.resources import contents
return contents('calibre_extensions')
def __len__(self):
return len(self.plugins)
from importlib.resources import contents
ans = 0
for x in contents('calibre_extensions'):
ans += 1
return ans
def __contains__(self, name):
return name in self.plugins
from importlib.resources import contents
for x in contents('calibre_extensions'):
if x == name:
return True
return False
def __getitem__(self, name):
if name not in self.plugins:
from importlib import import_module
try:
return import_module('calibre_extensions.' + name), None
except ModuleNotFoundError:
raise KeyError('No plugin named %r'%name)
self.load_plugin(name)
return self._plugins[name]
except Exception as err:
return None, str(err)
plugins = None

View File

@ -21,7 +21,7 @@ builtins.__dict__['__'] = lambda s: s
builtins.__dict__['dynamic_property'] = lambda func: func(None)
from calibre.constants import iswindows, preferred_encoding, plugins, ismacos, islinux, DEBUG, isfreebsd
from calibre.constants import iswindows, preferred_encoding, plugins, ismacos, islinux, ishaiku, DEBUG, isfreebsd, plugins_loc
_run_once = False
winutil = winutilerror = None
@ -49,7 +49,7 @@ def get_debug_executable():
if not _run_once:
_run_once = True
from importlib.machinery import ModuleSpec
from importlib.machinery import ModuleSpec, EXTENSION_SUFFIXES, ExtensionFileLoader
from importlib.util import find_spec
from importlib import import_module
@ -81,6 +81,89 @@ if not _run_once:
sys.meta_path.insert(0, DeVendor())
class ExtensionsPackageLoader:
def __init__(self, calibre_extensions):
self.calibre_extensions = calibre_extensions
def is_package(self, fullname=None):
return True
def get_resource_reader(self, fullname=None):
return self
def get_source(self, fullname=None):
return ''
def contents(self):
return iter(self.calibre_extensions)
def create_module(self, spec):
pass
def exec_module(self, spec):
pass
class ExtensionsImporter:
def __init__(self):
extensions = (
'pictureflow',
'lzx',
'msdes',
'podofo',
'cPalmdoc',
'progress_indicator',
'icu',
'speedup',
'html_as_json',
'unicode_names',
'html_syntax_highlighter',
'hyphen',
'freetype',
'imageops',
'hunspell',
'_patiencediff_c',
'bzzdec',
'matcher',
'tokenizer',
'certgen',
)
if iswindows:
extra = ('winutil', 'wpd', 'winfonts')
elif ismacos:
extra = ('usbobserver', 'cocoa')
elif isfreebsd or ishaiku or islinux or ismacos:
extra = ('libusb', 'libmtp')
else:
extra = ()
self.calibre_extensions = frozenset(extensions + extra)
def find_spec(self, fullname, path=None, target=None):
if not fullname.startswith('calibre_extensions'):
return
parts = fullname.split('.')
if parts[0] != 'calibre_extensions':
return
if len(parts) > 2:
return
is_package = len(parts) == 1
extension_name = None if is_package else parts[1]
path = os.path.join(plugins_loc, '__init__.py')
if extension_name:
if extension_name not in self.calibre_extensions:
return
for suffix in EXTENSION_SUFFIXES:
path = os.path.join(plugins_loc, extension_name + suffix)
if os.path.exists(path):
break
else:
return
return ModuleSpec(fullname, ExtensionFileLoader(fullname, path), is_package=is_package, origin=path)
return ModuleSpec(fullname, ExtensionsPackageLoader(self.calibre_extensions), is_package=is_package, origin=path)
sys.meta_path.append(ExtensionsImporter())
# Ensure that all temp files/dirs are created under a calibre tmp dir
from calibre.ptempfile import base_dir
try: