From bbd493dee79ddb524b580377b1ba764a792594a5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 22 Mar 2011 13:48:33 -0600 Subject: [PATCH] Replace use of __import__ with the much nicer importlib. calibre now *requires* python 2.7 --- src/calibre/constants.py | 12 +- src/calibre/customize/__init__.py | 6 +- src/calibre/devices/usbms/device.py | 2 +- src/calibre/ebooks/markdown/markdown.py | 129 +++++++++------------ src/calibre/gui2/convert/__init__.py | 6 +- src/calibre/gui2/convert/bulk.py | 6 +- src/calibre/gui2/convert/single.py | 10 +- src/calibre/gui2/dialogs/catalog.py | 7 +- src/calibre/gui2/preferences/conversion.py | 6 +- src/calibre/linux.py | 4 +- src/calibre/utils/ipc/worker.py | 4 +- 11 files changed, 87 insertions(+), 105 deletions(-) diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 3a8425d587..45bd351986 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -5,7 +5,7 @@ __appname__ = 'calibre' __version__ = '0.7.50' __author__ = "Kovid Goyal " -import re +import re, importlib _ver = __version__.split('.') _ver = [int(re.search(r'(\d+)', x).group(1)) for x in _ver] numeric_version = tuple(_ver) @@ -33,10 +33,10 @@ try: except: preferred_encoding = 'utf-8' -win32event = __import__('win32event') if iswindows else None -winerror = __import__('winerror') if iswindows else None -win32api = __import__('win32api') if iswindows else None -fcntl = None if iswindows else __import__('fcntl') +win32event = importlib.import_module('win32event') if iswindows else None +winerror = importlib.import_module('winerror') if iswindows else None +win32api = importlib.import_module('win32api') if iswindows else None +fcntl = None if iswindows else importlib.import_module('fcntl') filesystem_encoding = sys.getfilesystemencoding() if filesystem_encoding is None: filesystem_encoding = 'utf-8' @@ -74,7 +74,7 @@ if plugins is None: (['winutil'] if iswindows else []) + \ (['usbobserver'] if isosx else []): try: - p, err = __import__(plugin), '' + p, err = importlib.import_module(plugin), '' except Exception, err: p = None err = str(err) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 1f44eb4ae2..67cf822a17 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -2,7 +2,7 @@ from __future__ import with_statement __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -import os, sys, zipfile +import os, sys, zipfile, importlib from calibre.constants import numeric_version from calibre.ptempfile import PersistentTemporaryFile @@ -517,7 +517,7 @@ class InterfaceActionBase(Plugin): # {{{ This method must return the actual interface action plugin object. ''' mod, cls = self.actual_plugin.split(':') - return getattr(__import__(mod, fromlist=['1'], level=0), cls)(gui, + return getattr(importlib.import_module(mod), cls)(gui, self.site_customization) # }}} @@ -575,7 +575,7 @@ class PreferencesPlugin(Plugin): # {{{ base, _, wc = self.config_widget.partition(':') if not wc: wc = 'ConfigWidget' - base = __import__(base, fromlist=[1]) + base = importlib.import_module(base) widget = getattr(base, wc) return widget(parent) diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 37b2b061e5..91bb6d7416 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -124,7 +124,7 @@ class Device(DeviceConfig, DevicePlugin): if not prefix: return 0, 0 prefix = prefix[:-1] - win32file = __import__('win32file', globals(), locals(), [], -1) + import win32file try: sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters = \ win32file.GetDiskFreeSpace(prefix) diff --git a/src/calibre/ebooks/markdown/markdown.py b/src/calibre/ebooks/markdown/markdown.py index e734079116..677047878a 100644 --- a/src/calibre/ebooks/markdown/markdown.py +++ b/src/calibre/ebooks/markdown/markdown.py @@ -34,7 +34,7 @@ License: GPL 2 (http://www.gnu.org/copyleft/gpl.html) or BSD import re, sys, codecs from logging import getLogger, StreamHandler, Formatter, \ - DEBUG, INFO, WARN, ERROR, CRITICAL + DEBUG, INFO, WARN, CRITICAL MESSAGE_THRESHOLD = CRITICAL @@ -95,7 +95,7 @@ def removeBOM(text, encoding): # and uses the actual name of the executable called.) EXECUTABLE_NAME_FOR_USAGE = "python markdown.py" - + # --------------- CONSTANTS YOU _SHOULD NOT_ HAVE TO CHANGE ---------- @@ -242,8 +242,6 @@ class Element: if bidi: - orig_bidi = self.bidi - if not self.bidi or self.isDocumentElement: # Once the bidi is set don't change it (except for doc element) self.bidi = bidi @@ -319,7 +317,7 @@ class Element: childBuffer += "/>" - + buffer += "<" + self.nodeName if self.nodeName in ['p', 'li', 'ul', 'ol', @@ -330,10 +328,10 @@ class Element: bidi = self.bidi else: bidi = self.doc.bidi - + if bidi=="rtl": self.setAttribute("dir", "rtl") - + for attr in self.attributes: value = self.attribute_values[attr] value = self.doc.normalizeEntities(value, @@ -358,7 +356,7 @@ class TextNode: attrRegExp = re.compile(r'\{@([^\}]*)=([^\}]*)}') # {@id=123} def __init__ (self, text): - self.value = text + self.value = text def attributeCallback(self, match): @@ -372,7 +370,7 @@ class TextNode: text = self.value self.parent.setBidi(getBidiType(text)) - + if not text.startswith(HTML_PLACEHOLDER_PREFIX): if self.parent.nodeName == "p": text = text.replace("\n", "\n ") @@ -413,11 +411,11 @@ There are two types of preprocessors: TextPreprocessor and Preprocessor. class TextPreprocessor: ''' TextPreprocessors are run before the text is broken into lines. - + Each TextPreprocessor implements a "run" method that takes a pointer to a text string of the document, modifies it as necessary and returns - either the same pointer or a pointer to a new string. - + either the same pointer or a pointer to a new string. + TextPreprocessors must extend markdown.TextPreprocessor. ''' @@ -431,18 +429,18 @@ class Preprocessor: Each preprocessor implements a "run" method that takes a pointer to a list of lines of the document, modifies it as necessary and returns - either the same pointer or a pointer to a new list. - + either the same pointer or a pointer to a new list. + Preprocessors must extend markdown.Preprocessor. ''' def run(self, lines): pass - + class HtmlBlockPreprocessor(TextPreprocessor): """Removes html blocks from the source text and stores it.""" - + def _get_left_tag(self, block): return block[1:].replace(">", " ", 1).split()[0].lower() @@ -451,7 +449,7 @@ class HtmlBlockPreprocessor(TextPreprocessor): return block.rstrip()[-len(left_tag)-2:-1].lower() def _equal_tags(self, left_tag, right_tag): - + if left_tag == 'div' or left_tag[0] in ['?', '@', '%']: # handle PHP, etc. return True if ("/" + left_tag) == right_tag: @@ -467,17 +465,17 @@ class HtmlBlockPreprocessor(TextPreprocessor): def _is_oneliner(self, tag): return (tag in ['hr', 'hr/']) - + def run(self, text): new_blocks = [] text = text.split("\n\n") - + items = [] left_tag = '' right_tag = '' in_tag = False # flag - + for block in text: if block.startswith("\n"): block = block[1:] @@ -485,7 +483,7 @@ class HtmlBlockPreprocessor(TextPreprocessor): if not in_tag: if block.startswith("<"): - + left_tag = self._get_left_tag(block) right_tag = self._get_right_tag(left_tag, block) @@ -497,13 +495,13 @@ class HtmlBlockPreprocessor(TextPreprocessor): if self._is_oneliner(left_tag): new_blocks.append(block.strip()) continue - + if block[1] == "!": # is a comment block left_tag = "--" right_tag = self._get_right_tag(left_tag, block) # keep checking conditions below and maybe just append - + if block.rstrip().endswith(">") \ and self._equal_tags(left_tag, right_tag): new_blocks.append( @@ -519,9 +517,9 @@ class HtmlBlockPreprocessor(TextPreprocessor): else: items.append(block.strip()) - + right_tag = self._get_right_tag(left_tag, block) - + if self._equal_tags(left_tag, right_tag): # if find closing tag in_tag = False @@ -532,7 +530,7 @@ class HtmlBlockPreprocessor(TextPreprocessor): if items: new_blocks.append(self.stash.store('\n\n'.join(items))) new_blocks.append('\n') - + return "\n\n".join(new_blocks) HTML_BLOCK_PREPROCESSOR = HtmlBlockPreprocessor() @@ -605,7 +603,7 @@ LINE_PREPROCESSOR = LinePreprocessor() class ReferencePreprocessor(Preprocessor): - ''' + ''' Removes reference definitions from the text and stores them for later use. ''' @@ -760,7 +758,7 @@ class BacktickPattern (Pattern): return el -class DoubleTagPattern (SimpleTagPattern): +class DoubleTagPattern (SimpleTagPattern): def handleMatch(self, m, doc): tag1, tag2 = self.tag.split(",") @@ -775,7 +773,6 @@ class HtmlPattern (Pattern): def handleMatch (self, m, doc): rawhtml = m.group(2) - inline = True place_holder = self.stash.store(rawhtml) return doc.createTextNode(place_holder) @@ -926,11 +923,11 @@ There are two types of post-processors: Postprocessor and TextPostprocessor class Postprocessor: ''' Postprocessors are run before the dom it converted back into text. - + Each Postprocessor implements a "run" method that takes a pointer to a - NanoDom document, modifies it as necessary and returns a NanoDom + NanoDom document, modifies it as necessary and returns a NanoDom document. - + Postprocessors must extend markdown.Postprocessor. There are currently no standard post-processors, but the footnote @@ -945,10 +942,10 @@ class Postprocessor: class TextPostprocessor: ''' TextPostprocessors are run after the dom it converted back into text. - + Each TextPostprocessor implements a "run" method that takes a pointer to a text string, modifies it as necessary and returns a text string. - + TextPostprocessors must extend markdown.TextPostprocessor. ''' @@ -971,7 +968,7 @@ class RawHtmlTextPostprocessor(TextPostprocessor): html = '' else: html = HTML_REMOVED_TEXT - + text = text.replace("

%s\n

" % (HTML_PLACEHOLDER % i), html + "\n") text = text.replace(HTML_PLACEHOLDER % i, html) @@ -1031,7 +1028,6 @@ class BlockGuru: remainder of the original list""" items = [] - item = -1 i = 0 # to keep track of where we are @@ -1187,7 +1183,7 @@ class Markdown: RAWHTMLTEXTPOSTPROCESSOR] self.prePatterns = [] - + self.inlinePatterns = [DOUBLE_BACKTICK_PATTERN, BACKTICK_PATTERN, @@ -1241,7 +1237,7 @@ class Markdown: configs_for_ext = configs[ext] else: configs_for_ext = [] - extension = module.makeExtension(configs_for_ext) + extension = module.makeExtension(configs_for_ext) extension.extendMarkdown(self, globals()) @@ -1310,7 +1306,7 @@ class Markdown: else: buffer.append(line) self._processSection(self.top_element, buffer) - + #self._processSection(self.top_element, self.lines) # Not sure why I put this in but let's leave it for now. @@ -1426,7 +1422,7 @@ class Markdown: for item in list: el.appendChild(item) - + def _processUList(self, parent_elem, lines, inList): self._processList(parent_elem, lines, inList, @@ -1458,7 +1454,7 @@ class Markdown: i = 0 # a counter to keep track of where we are - for line in lines: + for line in lines: loose = 0 if not line.strip(): @@ -1477,7 +1473,7 @@ class Markdown: # Check if the next non-blank line is still a part of the list if ( RE.regExp['ul'].match(next) or - RE.regExp['ol'].match(next) or + RE.regExp['ol'].match(next) or RE.regExp['tabbed'].match(next) ): # get rid of any white space in the line items[item].append(line.strip()) @@ -1618,7 +1614,7 @@ class Markdown: i = 0 while i < len(parts): - + x = parts[i] if isinstance(x, (str, unicode)): @@ -1641,14 +1637,14 @@ class Markdown: parts[i] = self.doc.createTextNode(x) return parts - + def _applyPattern(self, line, pattern, patternIndex): """ Given a pattern name, this function checks if the line fits the pattern, creates the necessary elements, and returns back a list consisting of NanoDom elements and/or strings. - + @param line: the text to be processed @param pattern: the pattern to be checked @@ -1676,19 +1672,19 @@ class Markdown: if not node.nodeName in ["code", "pre"]: for child in node.childNodes: if isinstance(child, TextNode): - + result = self._handleInline(child.value, patternIndex+1) - + if result: if result == [child]: continue - + result.reverse() #to make insertion easier position = node.childNodes.index(child) - + node.removeChild(child) for item in result: @@ -1699,7 +1695,7 @@ class Markdown: self.doc.createTextNode(item)) else: node.insertChild(position, item) - + @@ -1798,14 +1794,14 @@ def markdownFromFile(input = None, def markdown(text, extensions = [], safe_mode = False): - + message(DEBUG, "in markdown.markdown(), received text:\n%s" % text) extension_names = [] extension_configs = {} - + for ext in extensions: - pos = ext.find("(") + pos = ext.find("(") if pos == -1: extension_names.append(ext) else: @@ -1820,7 +1816,7 @@ def markdown(text, safe_mode = safe_mode) return md.convert(text) - + class Extension: @@ -1845,26 +1841,11 @@ Python 2.3 or higher required for advanced command line options. For lower versions of Python use: %s INPUT_FILE > OUTPUT_FILE - + """ % EXECUTABLE_NAME_FOR_USAGE def parse_options(): - - try: - optparse = __import__("optparse") - except: - if len(sys.argv) == 2: - return {'input': sys.argv[1], - 'output': None, - 'message_threshold': CRITICAL, - 'safe': False, - 'extensions': [], - 'encoding': None } - - else: - print OPTPARSE_WARNING - return None - + import optparse parser = optparse.OptionParser(usage="%prog INPUTFILE [options]") parser.add_option("-f", "--file", dest="filename", @@ -1881,7 +1862,7 @@ def parse_options(): parser.add_option("-s", "--safe", dest="safe", default=False, metavar="SAFE_MODE", help="same mode ('replace', 'remove' or 'escape' user's HTML tag)") - + parser.add_option("--noisy", action="store_const", const=DEBUG, dest="verbose", help="print debug messages") @@ -1914,14 +1895,14 @@ def main(): if not options: sys.exit(0) - + markdownFromFile(**options) if __name__ == '__main__': sys.exit(main()) """ Run Markdown from the command line. """ - + diff --git a/src/calibre/gui2/convert/__init__.py b/src/calibre/gui2/convert/__init__.py index 925fecd693..bdcf9ede05 100644 --- a/src/calibre/gui2/convert/__init__.py +++ b/src/calibre/gui2/convert/__init__.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import textwrap, codecs +import textwrap, codecs, importlib from functools import partial from PyQt4.Qt import QWidget, QSpinBox, QDoubleSpinBox, QLineEdit, QTextEdit, \ @@ -22,8 +22,8 @@ from calibre.customize.ui import plugin_for_input_format def config_widget_for_input_plugin(plugin): name = plugin.name.lower().replace(' ', '_') try: - return __import__('calibre.gui2.convert.'+name, - fromlist=[1]).PluginWidget + return importlib.import_module( + 'calibre.gui2.convert.'+name).PluginWidget except ImportError: pass diff --git a/src/calibre/gui2/convert/bulk.py b/src/calibre/gui2/convert/bulk.py index 349f39ac76..576b3ca3e7 100644 --- a/src/calibre/gui2/convert/bulk.py +++ b/src/calibre/gui2/convert/bulk.py @@ -4,7 +4,7 @@ __license__ = 'GPL 3' __copyright__ = '2009, John Schember ' __docformat__ = 'restructuredtext en' -import shutil +import shutil, importlib from PyQt4.Qt import QString, SIGNAL @@ -82,8 +82,8 @@ class BulkConfig(Config): output_widget = None name = self.plumber.output_plugin.name.lower().replace(' ', '_') try: - output_widget = __import__('calibre.gui2.convert.'+name, - fromlist=[1]) + output_widget = importlib.import_module( + 'calibre.gui2.convert.'+name) pw = output_widget.PluginWidget pw.ICON = I('back.png') pw.HELP = _('Options specific to the output format.') diff --git a/src/calibre/gui2/convert/single.py b/src/calibre/gui2/convert/single.py index 59fcbb65ad..3575fb5ffb 100644 --- a/src/calibre/gui2/convert/single.py +++ b/src/calibre/gui2/convert/single.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import sys, cPickle, shutil +import sys, cPickle, shutil, importlib from PyQt4.Qt import QString, SIGNAL, QAbstractListModel, Qt, QVariant, QFont @@ -182,8 +182,8 @@ class Config(ResizableDialog, Ui_Dialog): output_widget = None name = self.plumber.output_plugin.name.lower().replace(' ', '_') try: - output_widget = __import__('calibre.gui2.convert.'+name, - fromlist=[1]) + output_widget = importlib.import_module( + 'calibre.gui2.convert.'+name) pw = output_widget.PluginWidget pw.ICON = I('back.png') pw.HELP = _('Options specific to the output format.') @@ -193,8 +193,8 @@ class Config(ResizableDialog, Ui_Dialog): input_widget = None name = self.plumber.input_plugin.name.lower().replace(' ', '_') try: - input_widget = __import__('calibre.gui2.convert.'+name, - fromlist=[1]) + input_widget = importlib.import_module( + 'calibre.gui2.convert.'+name) pw = input_widget.PluginWidget pw.ICON = I('forward.png') pw.HELP = _('Options specific to the input format.') diff --git a/src/calibre/gui2/dialogs/catalog.py b/src/calibre/gui2/dialogs/catalog.py index ebca7235eb..a8f7ed160f 100644 --- a/src/calibre/gui2/dialogs/catalog.py +++ b/src/calibre/gui2/dialogs/catalog.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os, sys +import os, sys, importlib from calibre.customize.ui import config from calibre.gui2.dialogs.catalog_ui import Ui_Dialog @@ -43,8 +43,7 @@ class Catalog(ResizableDialog, Ui_Dialog): name = plugin.name.lower().replace(' ', '_') if type(plugin) in builtin_plugins: try: - catalog_widget = __import__('calibre.gui2.catalog.'+name, - fromlist=[1]) + catalog_widget = importlib.import_module('calibre.gui2.catalog.'+name) pw = catalog_widget.PluginWidget() pw.initialize(name, db) pw.ICON = I('forward.png') @@ -75,7 +74,7 @@ class Catalog(ResizableDialog, Ui_Dialog): # Import the dynamic PluginWidget() from .py file provided in plugin.zip try: sys.path.insert(0, plugin.resources_path) - catalog_widget = __import__(name, fromlist=[1]) + catalog_widget = importlib.import_module(name) pw = catalog_widget.PluginWidget() pw.initialize(name) pw.ICON = I('forward.png') diff --git a/src/calibre/gui2/preferences/conversion.py b/src/calibre/gui2/preferences/conversion.py index 8de9ee1661..b5240227d3 100644 --- a/src/calibre/gui2/preferences/conversion.py +++ b/src/calibre/gui2/preferences/conversion.py @@ -5,6 +5,8 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' +import importlib + from PyQt4.Qt import QIcon, Qt, QStringListModel, QVariant from calibre.gui2.preferences import ConfigWidgetBase, test_widget, AbortCommit @@ -104,8 +106,8 @@ class OutputOptions(Base): for plugin in output_format_plugins(): name = plugin.name.lower().replace(' ', '_') try: - output_widget = __import__('calibre.gui2.convert.'+name, - fromlist=[1]) + output_widget = importlib.import_module( + 'calibre.gui2.convert.'+name) pw = output_widget.PluginWidget self.conversion_widgets.append(pw) except ImportError: diff --git a/src/calibre/linux.py b/src/calibre/linux.py index 22f8af56c2..96f0fbd92d 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -3,7 +3,7 @@ __copyright__ = '2008, Kovid Goyal ' ''' Post installation script for linux ''' -import sys, os, cPickle, textwrap, stat +import sys, os, cPickle, textwrap, stat, importlib from subprocess import check_call from calibre import __appname__, prints, guess_type @@ -309,7 +309,7 @@ class PostInstall: for src in entry_points['console_scripts']: prog, right = src.split('=') prog = prog.strip() - module = __import__(right.split(':')[0].strip(), fromlist=['a']) + module = importlib.import_module(right.split(':')[0].strip()) parser = getattr(module, 'option_parser', None) if parser is None: continue diff --git a/src/calibre/utils/ipc/worker.py b/src/calibre/utils/ipc/worker.py index e187235a9e..9594f64ae4 100644 --- a/src/calibre/utils/ipc/worker.py +++ b/src/calibre/utils/ipc/worker.py @@ -6,7 +6,7 @@ __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os, cPickle, sys +import os, cPickle, sys, importlib from multiprocessing.connection import Client from threading import Thread from Queue import Queue @@ -75,7 +75,7 @@ class Progress(Thread): def get_func(name): module, func, notification = PARALLEL_FUNCS[name] - module = __import__(module, fromlist=[1]) + module = importlib.import_module(module) func = getattr(module, func) return func, notification