mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Sync to trunk.
This commit is contained in:
commit
1df045e236
@ -19,6 +19,13 @@
|
|||||||
# new recipes:
|
# new recipes:
|
||||||
# - title:
|
# - title:
|
||||||
|
|
||||||
|
- version: 0.7.52
|
||||||
|
date: 2011-03-25
|
||||||
|
|
||||||
|
bug fixes:
|
||||||
|
- title: "Fixes a typo in 0.7.51 that broke the downloading of some news. Apologies."
|
||||||
|
tickets: [742840]
|
||||||
|
|
||||||
- version: 0.7.51
|
- version: 0.7.51
|
||||||
date: 2011-03-25
|
date: 2011-03-25
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class AdvancedUserRecipe1291143841(BasicNewsRecipe):
|
class AdvancedUserRecipe1291143841(BasicNewsRecipe):
|
||||||
title = u'Poughkeepsipe Journal'
|
title = u'Poughkeepsie Journal'
|
||||||
language = 'en'
|
language = 'en'
|
||||||
__author__ = 'weebl'
|
__author__ = 'weebl'
|
||||||
oldest_article = 7
|
oldest_article = 7
|
||||||
|
@ -14,7 +14,7 @@ from setup.build_environment import HOST, PROJECT
|
|||||||
BASE_RSYNC = ['rsync', '-avz', '--delete']
|
BASE_RSYNC = ['rsync', '-avz', '--delete']
|
||||||
EXCLUDES = []
|
EXCLUDES = []
|
||||||
for x in [
|
for x in [
|
||||||
'src/calibre/plugins', 'src/calibre/manual', 'src/calibre/trac', 'recipes',
|
'src/calibre/plugins', 'src/calibre/manual', 'src/calibre/trac',
|
||||||
'.bzr', '.build', '.svn', 'build', 'dist', 'imgsrc', '*.pyc', '*.pyo', '*.swp',
|
'.bzr', '.build', '.svn', 'build', 'dist', 'imgsrc', '*.pyc', '*.pyo', '*.swp',
|
||||||
'*.swo', 'format_docs']:
|
'*.swo', 'format_docs']:
|
||||||
EXCLUDES.extend(['--exclude', x])
|
EXCLUDES.extend(['--exclude', x])
|
||||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.7.51'
|
__version__ = '0.7.52'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
|
|
||||||
import re, importlib
|
import re, importlib
|
||||||
|
@ -4,9 +4,22 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
import os, sys, zipfile, importlib
|
import os, sys, zipfile, importlib
|
||||||
|
|
||||||
from calibre.constants import numeric_version
|
from calibre.constants import numeric_version, iswindows, isosx
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
|
||||||
|
platform = 'linux'
|
||||||
|
if iswindows:
|
||||||
|
platform = 'windows'
|
||||||
|
elif isosx:
|
||||||
|
platform = 'osx'
|
||||||
|
|
||||||
|
|
||||||
|
class PluginNotFound(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class InvalidPlugin(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Plugin(object): # {{{
|
class Plugin(object): # {{{
|
||||||
'''
|
'''
|
||||||
|
@ -2,17 +2,16 @@ from __future__ import with_statement
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
import os, shutil, traceback, functools, sys, re
|
import os, shutil, traceback, functools, sys
|
||||||
from contextlib import closing
|
|
||||||
|
|
||||||
from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \
|
from calibre.customize import (CatalogPlugin, FileTypePlugin, PluginNotFound,
|
||||||
MetadataReaderPlugin, MetadataWriterPlugin, \
|
MetadataReaderPlugin, MetadataWriterPlugin,
|
||||||
InterfaceActionBase as InterfaceAction, \
|
InterfaceActionBase as InterfaceAction,
|
||||||
PreferencesPlugin
|
PreferencesPlugin, platform, InvalidPlugin)
|
||||||
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
|
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
|
||||||
|
from calibre.customize.zipplugin import loader
|
||||||
from calibre.customize.profiles import InputProfile, OutputProfile
|
from calibre.customize.profiles import InputProfile, OutputProfile
|
||||||
from calibre.customize.builtins import plugins as builtin_plugins
|
from calibre.customize.builtins import plugins as builtin_plugins
|
||||||
from calibre.constants import numeric_version as version, iswindows, isosx
|
|
||||||
from calibre.devices.interface import DevicePlugin
|
from calibre.devices.interface import DevicePlugin
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
from calibre.ebooks.metadata.covers import CoverDownload
|
from calibre.ebooks.metadata.covers import CoverDownload
|
||||||
@ -22,14 +21,6 @@ from calibre.utils.config import make_config_dir, Config, ConfigProxy, \
|
|||||||
from calibre.ebooks.epub.fix import ePubFixer
|
from calibre.ebooks.epub.fix import ePubFixer
|
||||||
from calibre.ebooks.metadata.sources.base import Source
|
from calibre.ebooks.metadata.sources.base import Source
|
||||||
|
|
||||||
platform = 'linux'
|
|
||||||
if iswindows:
|
|
||||||
platform = 'windows'
|
|
||||||
elif isosx:
|
|
||||||
platform = 'osx'
|
|
||||||
|
|
||||||
from zipfile import ZipFile
|
|
||||||
|
|
||||||
def _config():
|
def _config():
|
||||||
c = Config('customize')
|
c = Config('customize')
|
||||||
c.add_opt('plugins', default={}, help=_('Installed plugins'))
|
c.add_opt('plugins', default={}, help=_('Installed plugins'))
|
||||||
@ -42,11 +33,6 @@ def _config():
|
|||||||
|
|
||||||
config = _config()
|
config = _config()
|
||||||
|
|
||||||
class InvalidPlugin(ValueError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class PluginNotFound(ValueError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def find_plugin(name):
|
def find_plugin(name):
|
||||||
for plugin in _initialized_plugins:
|
for plugin in _initialized_plugins:
|
||||||
@ -60,38 +46,7 @@ def load_plugin(path_to_zip_file): # {{{
|
|||||||
|
|
||||||
:return: A :class:`Plugin` instance.
|
:return: A :class:`Plugin` instance.
|
||||||
'''
|
'''
|
||||||
#print 'Loading plugin from', path_to_zip_file
|
return loader.load(path_to_zip_file)
|
||||||
if not os.access(path_to_zip_file, os.R_OK):
|
|
||||||
raise PluginNotFound
|
|
||||||
with closing(ZipFile(path_to_zip_file)) as zf:
|
|
||||||
for name in zf.namelist():
|
|
||||||
if name.lower().endswith('plugin.py'):
|
|
||||||
locals = {}
|
|
||||||
raw = zf.read(name)
|
|
||||||
lines, encoding = raw.splitlines(), 'utf-8'
|
|
||||||
cr = re.compile(r'coding[:=]\s*([-\w.]+)')
|
|
||||||
raw = []
|
|
||||||
for l in lines[:2]:
|
|
||||||
match = cr.search(l)
|
|
||||||
if match is not None:
|
|
||||||
encoding = match.group(1)
|
|
||||||
else:
|
|
||||||
raw.append(l)
|
|
||||||
raw += lines[2:]
|
|
||||||
raw = '\n'.join(raw)
|
|
||||||
raw = raw.decode(encoding)
|
|
||||||
raw = re.sub('\r\n', '\n', raw)
|
|
||||||
exec raw in locals
|
|
||||||
for x in locals.values():
|
|
||||||
if isinstance(x, type) and issubclass(x, Plugin) and \
|
|
||||||
x.name != 'Trivial Plugin':
|
|
||||||
if x.minimum_calibre_version > version or \
|
|
||||||
platform not in x.supported_platforms:
|
|
||||||
continue
|
|
||||||
|
|
||||||
return x
|
|
||||||
|
|
||||||
raise InvalidPlugin(_('No valid plugin found in ')+path_to_zip_file)
|
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
154
src/calibre/customize/zipplugin.py
Normal file
154
src/calibre/customize/zipplugin.py
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import os, zipfile, posixpath, importlib, threading, re
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from calibre.customize import (Plugin, numeric_version, platform,
|
||||||
|
InvalidPlugin, PluginNotFound)
|
||||||
|
|
||||||
|
# PEP 302 based plugin loading mechanism, works around the bug in zipimport in
|
||||||
|
# python 2.x that prevents importing from zip files in locations whose paths
|
||||||
|
# have non ASCII characters
|
||||||
|
|
||||||
|
|
||||||
|
class PluginLoader(object):
|
||||||
|
|
||||||
|
'''
|
||||||
|
The restrictions that a zip file must obey to be a valid calibre plugin
|
||||||
|
are:
|
||||||
|
|
||||||
|
* The .py file that defines the main plugin class must have a name
|
||||||
|
that:
|
||||||
|
* Ends in plugin.py
|
||||||
|
* Is a valid python identifier (contains only English alphabets,
|
||||||
|
underscores and numbers and starts with an alphabet). This
|
||||||
|
applies to the file name minus the .py extension, obviously.
|
||||||
|
* Try to make this name as distinct as possible, as it will be
|
||||||
|
put into a global namespace of all plugins.
|
||||||
|
* The zip file must contain a .py file that defines the main plugin
|
||||||
|
class at the top level. That is, it must not be in a subdirectory.
|
||||||
|
The filename must follow the restrictions outlined above.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.loaded_plugins = {}
|
||||||
|
self._lock = threading.RLock()
|
||||||
|
self._identifier_pat = re.compile(r'[a-zA-Z][_0-9a-zA-Z]*')
|
||||||
|
|
||||||
|
def load(self, path_to_zip_file):
|
||||||
|
if not os.access(path_to_zip_file, os.R_OK):
|
||||||
|
raise PluginNotFound('Cannot access %r'%path_to_zip_file)
|
||||||
|
|
||||||
|
with zipfile.ZipFile(path_to_zip_file) as zf:
|
||||||
|
plugin_name = self._locate_code(zf, path_to_zip_file)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ans = None
|
||||||
|
m = importlib.import_module(
|
||||||
|
'calibre_plugins.%s.__init__'%plugin_name)
|
||||||
|
for obj in m.__dict__.itervalues():
|
||||||
|
if isinstance(obj, type) and issubclass(obj, Plugin) and \
|
||||||
|
obj.name != 'Trivial Plugin':
|
||||||
|
ans = obj
|
||||||
|
break
|
||||||
|
if ans is None:
|
||||||
|
raise InvalidPlugin('No plugin class found in %r:%r'%(
|
||||||
|
path_to_zip_file, plugin_name))
|
||||||
|
|
||||||
|
if ans.minimum_calibre_version < numeric_version:
|
||||||
|
raise InvalidPlugin(
|
||||||
|
'The plugin at %r needs a version of calibre >= %r' %
|
||||||
|
(path_to_zip_file, '.'.join(ans.minimum_calibre_version)))
|
||||||
|
|
||||||
|
if platform not in ans.supported_platforms:
|
||||||
|
raise InvalidPlugin(
|
||||||
|
'The plugin at %r cannot be used on %s' %
|
||||||
|
(path_to_zip_file, platform))
|
||||||
|
|
||||||
|
return ans
|
||||||
|
except:
|
||||||
|
with self._lock:
|
||||||
|
del self.loaded_plugins[plugin_name]
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def _locate_code(self, zf, path_to_zip_file):
|
||||||
|
names = [x if isinstance(x, unicode) else x.decode('utf-8') for x in
|
||||||
|
zf.namelist()]
|
||||||
|
names = [x[1:] if x[0] == '/' else x for x in names]
|
||||||
|
|
||||||
|
plugin_name = None
|
||||||
|
for name in names:
|
||||||
|
name, ext = posixpath.splitext(name)
|
||||||
|
if name.startswith('plugin-import-name-') and ext == '.txt':
|
||||||
|
plugin_name = name.rpartition('-')[-1]
|
||||||
|
|
||||||
|
if plugin_name is None:
|
||||||
|
c = 0
|
||||||
|
while True:
|
||||||
|
c += 1
|
||||||
|
plugin_name = 'dummy%d'%c
|
||||||
|
if plugin_name not in self.loaded_plugins:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if plugin_name in self.loaded_plugins:
|
||||||
|
raise InvalidPlugin((
|
||||||
|
'The plugin in %r uses an import name %r that is already'
|
||||||
|
' used by another plugin') % (path_to_zip_file, plugin_name))
|
||||||
|
if self._identifier_pat.match(plugin_name) is None:
|
||||||
|
raise InvalidPlugin((
|
||||||
|
'The plugin at %r uses an invalid import name: %r' %
|
||||||
|
(path_to_zip_file, plugin_name)))
|
||||||
|
|
||||||
|
pynames = [x for x in names if x.endswith('.py')]
|
||||||
|
|
||||||
|
candidates = [posixpath.dirname(x) for x in pynames if
|
||||||
|
x.endswith('/__init__.py')]
|
||||||
|
candidates.sort(key=lambda x: x.count('/'))
|
||||||
|
valid_packages = set()
|
||||||
|
|
||||||
|
for candidate in candidates:
|
||||||
|
parts = candidate.split('/')
|
||||||
|
parent = '.'.join(parts[:-1])
|
||||||
|
if parent and parent not in valid_packages:
|
||||||
|
continue
|
||||||
|
valid_packages.add('.'.join(parts))
|
||||||
|
|
||||||
|
names = OrderedDict()
|
||||||
|
|
||||||
|
for candidate in names:
|
||||||
|
parts = posixpath.splitext(candidate)[0].split('/')
|
||||||
|
package = '.'.join(parts[:-1])
|
||||||
|
if package and package not in valid_packages:
|
||||||
|
continue
|
||||||
|
name = '.'.join(parts)
|
||||||
|
names[name] = zf.getinfo(candidate)
|
||||||
|
|
||||||
|
# Legacy plugins
|
||||||
|
if '__init__' not in names:
|
||||||
|
for name in list(names.iterkeys()):
|
||||||
|
if '.' not in name and name.endswith('plugin'):
|
||||||
|
names['__init__'] = names[name]
|
||||||
|
break
|
||||||
|
|
||||||
|
if '__init__' not in names:
|
||||||
|
raise InvalidPlugin(('The plugin in %r is invalid. It does not '
|
||||||
|
'contain a top-level __init__.py file')
|
||||||
|
% path_to_zip_file)
|
||||||
|
|
||||||
|
with self._lock:
|
||||||
|
self.loaded_plugins[plugin_name] = (path_to_zip_file, names)
|
||||||
|
|
||||||
|
return plugin_name
|
||||||
|
|
||||||
|
|
||||||
|
loader = PluginLoader()
|
||||||
|
|
||||||
|
|
@ -4,9 +4,9 @@
|
|||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: calibre 0.7.51\n"
|
"Project-Id-Version: calibre 0.7.52\n"
|
||||||
"POT-Creation-Date: 2011-03-25 12:23+MDT\n"
|
"POT-Creation-Date: 2011-03-25 19:03+MDT\n"
|
||||||
"PO-Revision-Date: 2011-03-25 12:23+MDT\n"
|
"PO-Revision-Date: 2011-03-25 19:03+MDT\n"
|
||||||
"Last-Translator: Automatically generated\n"
|
"Last-Translator: Automatically generated\n"
|
||||||
"Language-Team: LANGUAGE\n"
|
"Language-Team: LANGUAGE\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user