From 764e8bff7e77ef83362c03211fdd08ee1f625b8d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 18 Nov 2019 14:03:58 +0530 Subject: [PATCH] Remove py3 conditionals --- manual/conf.py | 3 +- setup/__init__.py | 47 +- setup/build.py | 18 +- setup/extensions.json | 15 - setup/translations.py | 4 +- src/backports/__init__.py | 0 src/backports/functools_lru_cache.py | 199 --- src/calibre/__init__.py | 31 +- src/calibre/constants.py | 9 +- src/calibre/customize/zipplugin.py | 5 +- src/calibre/db/backend.py | 4 +- src/calibre/db/categories.py | 12 +- src/calibre/db/cli/main.py | 4 +- src/calibre/devices/kobo/bookmark.py | 4 +- src/calibre/devices/kobo/books.py | 5 +- src/calibre/devices/mtp/unix/driver.py | 13 +- src/calibre/ebooks/compression/tcr.py | 4 +- src/calibre/ebooks/html/input.py | 5 +- src/calibre/ebooks/lit/reader.py | 3 +- src/calibre/ebooks/lrf/objects.py | 59 +- src/calibre/ebooks/metadata/book/base.py | 10 +- src/calibre/ebooks/metadata/opf2.py | 10 +- src/calibre/ebooks/oeb/base.py | 36 +- src/calibre/ebooks/pdf/pdftohtml.py | 11 +- src/calibre/ebooks/pdf/render/common.py | 4 +- src/calibre/gui2/linux_file_dialogs.py | 9 +- src/calibre/library/database2.py | 7 +- src/calibre/linux.py | 8 +- src/calibre/ptempfile.py | 26 +- src/calibre/spell/dictionary.py | 4 +- src/calibre/srv/http_response.py | 19 +- src/calibre/srv/manage_users_cli.py | 4 +- src/calibre/srv/opts.py | 7 +- src/calibre/srv/routes.py | 8 +- src/calibre/srv/tests/base.py | 6 +- src/calibre/srv/tests/http.py | 3 +- src/calibre/srv/tests/loop.py | 6 +- src/calibre/srv/utils.py | 36 +- src/calibre/startup.py | 41 +- src/calibre/test_build.py | 8 +- src/calibre/utils/Zeroconf.py | 1654 ---------------------- src/calibre/utils/complete.py | 39 +- src/calibre/utils/config_base.py | 7 +- src/calibre/utils/dbus_service.py | 5 +- src/calibre/utils/filenames.py | 14 +- src/calibre/utils/imghdr.py | 9 +- src/calibre/utils/ipc/__init__.py | 4 +- src/calibre/utils/ipc/launch.py | 43 +- src/calibre/utils/ipc/server.py | 6 +- src/calibre/utils/localization.py | 7 +- src/calibre/utils/lock.py | 4 +- src/calibre/utils/mdns.py | 24 +- src/calibre/utils/monotonic.c | 88 -- src/calibre/utils/monotonic.py | 13 +- src/calibre/utils/serialize.py | 26 +- src/calibre/utils/shared_file.py | 10 +- src/calibre/utils/smtp.py | 6 +- src/calibre/utils/terminal.py | 10 +- src/calibre/utils/unicode_getpass.py | 40 - src/calibre/utils/zlib2.c | 426 ------ src/polyglot/functools.py | 8 +- src/polyglot/html_entities.py | 8 +- src/polyglot/http_client.py | 28 +- src/polyglot/http_cookie.py | 10 +- src/polyglot/http_server.py | 10 +- src/polyglot/plistlib.py | 25 +- src/polyglot/queue.py | 7 +- src/polyglot/reprlib.py | 8 +- src/polyglot/smtplib.py | 56 +- src/polyglot/socketserver.py | 7 +- src/polyglot/urllib.py | 52 +- 71 files changed, 192 insertions(+), 3169 deletions(-) delete mode 100644 src/backports/__init__.py delete mode 100644 src/backports/functools_lru_cache.py delete mode 100644 src/calibre/utils/Zeroconf.py delete mode 100644 src/calibre/utils/monotonic.c delete mode 100644 src/calibre/utils/unicode_getpass.py delete mode 100644 src/calibre/utils/zlib2.c diff --git a/manual/conf.py b/manual/conf.py index 9e61d4b34c..81aa89da0f 100644 --- a/manual/conf.py +++ b/manual/conf.py @@ -18,7 +18,6 @@ from datetime import date base = os.path.dirname(os.path.abspath(__file__)) sys.path.append(base) sys.path.insert(0, os.path.dirname(base)) -ispy3 = sys.version_info.major > 2 from setup import __appname__, __version__ import calibre.utils.localization as l # Ensure calibre translations are installed import custom @@ -101,7 +100,7 @@ if language not in {'en', 'eng'}: except IOError: pass else: - title = getattr(t, 'gettext' if ispy3 else 'ugettext')(title) + title = t.gettext(title) # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True diff --git a/setup/__init__.py b/setup/__init__.py index 9b12fb5d95..bf1365fe44 100644 --- a/setup/__init__.py +++ b/setup/__init__.py @@ -11,7 +11,6 @@ from contextlib import contextmanager is64bit = platform.architecture()[0] == '64bit' iswindows = re.search('win(32|64)', sys.platform) -ispy3 = sys.version_info.major > 2 isosx = 'darwin' in sys.platform isfreebsd = 'freebsd' in sys.platform isnetbsd = 'netbsd' in sys.platform @@ -128,52 +127,8 @@ def initialize_constants(): initialize_constants() - preferred_encoding = 'utf-8' - - -if ispy3: - prints = print -else: - def prints(*args, **kwargs): - ''' - Print unicode arguments safely by encoding them to preferred_encoding - Has the same signature as the print function from Python 3, except for the - additional keyword argument safe_encode, which if set to True will cause the - function to use repr when encoding fails. - ''' - file = kwargs.get('file', sys.stdout) - sep = kwargs.get('sep', ' ') - end = kwargs.get('end', '\n') - enc = preferred_encoding - safe_encode = kwargs.get('safe_encode', False) - for i, arg in enumerate(args): - if isinstance(arg, type(u'')): - try: - arg = arg.encode(enc) - except UnicodeEncodeError: - if not safe_encode: - raise - arg = repr(arg) - if not isinstance(arg, str): - try: - arg = str(arg) - except ValueError: - arg = type(u'')(arg) - if isinstance(arg, type(u'')): - try: - arg = arg.encode(enc) - except UnicodeEncodeError: - if not safe_encode: - raise - arg = repr(arg) - - file.write(arg) - if i != len(args)-1: - file.write(sep) - file.write(end) - - +prints = print warnings = [] diff --git a/setup/build.py b/setup/build.py index 8dd1f15726..8db1531606 100644 --- a/setup/build.py +++ b/setup/build.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai - - __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' @@ -10,14 +8,14 @@ __docformat__ = 'restructuredtext en' import textwrap, os, shlex, subprocess, glob, shutil, re, sys, json from collections import namedtuple -from setup import Command, islinux, isbsd, isfreebsd, isosx, ishaiku, SRC, iswindows, __version__, ispy3 +from setup import Command, islinux, isbsd, isfreebsd, isosx, ishaiku, SRC, iswindows, __version__ isunix = islinux or isosx or isbsd or ishaiku py_lib = os.path.join(sys.prefix, 'libs', 'python%d%d.lib' % sys.version_info[:2]) def init_symbol_name(name): - prefix = 'PyInit_' if ispy3 else 'init' + prefix = 'PyInit_' return prefix + name @@ -44,7 +42,7 @@ class Extension(object): if iswindows: self.cflags.append('/DCALIBRE_MODINIT_FUNC=PyMODINIT_FUNC') else: - return_type = 'PyObject*' if ispy3 else 'void' + return_type = 'PyObject*' extern_decl = 'extern "C"' if self.needs_cxx else '' self.cflags.append( @@ -220,12 +218,8 @@ def init_env(): class Build(Command): short_description = 'Build calibre C/C++ extension modules' - if ispy3: - DEFAULT_OUTPUTDIR = os.path.abspath(os.path.join(SRC, 'calibre', 'plugins', '3')) - DEFAULT_BUILDDIR = os.path.abspath(os.path.join(os.path.dirname(SRC), 'build', '3')) - else: - DEFAULT_OUTPUTDIR = os.path.abspath(os.path.join(SRC, 'calibre', 'plugins')) - DEFAULT_BUILDDIR = os.path.abspath(os.path.join(os.path.dirname(SRC), 'build')) + DEFAULT_OUTPUTDIR = os.path.abspath(os.path.join(SRC, 'calibre', 'plugins', sys.version_info.major)) + DEFAULT_BUILDDIR = os.path.abspath(os.path.join(os.path.dirname(SRC), 'build', sys.version_info.major)) description = textwrap.dedent('''\ calibre depends on several python extensions written in C/C++. @@ -275,8 +269,6 @@ class Build(Command): for ext in extensions: if opts.only != 'all' and opts.only != ext.name: continue - if ext.needs_py2 and ispy3: - continue if ext.error: if ext.optional: self.warn(ext.error) diff --git a/setup/extensions.json b/setup/extensions.json index 2474973b2a..ca9097cbd4 100644 --- a/setup/extensions.json +++ b/setup/extensions.json @@ -8,12 +8,6 @@ "windows_libraries": "libhunspell", "needs_c++11": true }, - { - "name": "monotonic", - "sources": "calibre/utils/monotonic.c", - "linux_libraries": "rt", - "needs_py2": true - }, { "name": "hyphen", "sources": "calibre/utils/hyphenation/hyphen.c", @@ -34,15 +28,6 @@ "libraries": "m", "windows_libraries": "" }, - { - "name": "zlib2", - "sources": "calibre/utils/zlib2.c", - "inc_dirs": "!zlib_inc_dirs", - "lib_dirs": "!zlib_lib_dirs", - "libraries": "z", - "windows_libraries": "zlib", - "needs_py2": true - }, { "name": "certgen", "sources": "calibre/utils/certgen.c", diff --git a/setup/translations.py b/setup/translations.py index 5c21dd6f83..60049c7a4d 100644 --- a/setup/translations.py +++ b/setup/translations.py @@ -11,7 +11,7 @@ from collections import defaultdict from locale import normalize as normalize_locale from functools import partial -from setup import Command, __appname__, __version__, require_git_master, build_cache_dir, edit_file, dump_json, ispy3 +from setup import Command, __appname__, __version__, require_git_master, build_cache_dir, edit_file, dump_json from setup.parallel_build import parallel_check_output from polyglot.builtins import codepoint_to_chr, iteritems, range is_ci = os.environ.get('CI', '').lower() == 'true' @@ -602,7 +602,7 @@ class Translations(POT): # {{{ lang_names = {} for l in dl: translator = translator_for_lang(l)['translator'] - t = getattr(translator, 'gettext' if ispy3 else 'ugettext') + t = translator.gettext t = partial(get_language, gettext_func=t) lang_names[l] = {x: t(x) for x in dl} zi = ZipInfo('lang-names.json') diff --git a/src/backports/__init__.py b/src/backports/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/backports/functools_lru_cache.py b/src/backports/functools_lru_cache.py deleted file mode 100644 index dd4335273c..0000000000 --- a/src/backports/functools_lru_cache.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python -# vim:fileencoding=utf-8 -# License: GPL v3 Copyright: 2019, Kovid Goyal - - - -import functools -from collections import namedtuple -from threading import RLock - -_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) - - -@functools.wraps(functools.update_wrapper) -def update_wrapper( - wrapper, - wrapped, - assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES -): - """ - Patch two bugs in functools.update_wrapper. - """ - # workaround for http://bugs.python.org/issue3445 - assigned = tuple(attr for attr in assigned if hasattr(wrapped, attr)) - wrapper = functools.update_wrapper(wrapper, wrapped, assigned, updated) - # workaround for https://bugs.python.org/issue17482 - wrapper.__wrapped__ = wrapped - return wrapper - - -class _HashedSeq(list): - __slots__ = 'hashvalue' - - def __init__(self, tup, hash=hash): - self[:] = tup - self.hashvalue = hash(tup) - - def __hash__(self): - return self.hashvalue - - -def _make_key( - args, - kwds, - typed, - kwd_mark=(object(), ), - fasttypes=set([int, str, frozenset, type(None)]), - sorted=sorted, - tuple=tuple, - type=type, - len=len -): - 'Make a cache key from optionally typed positional and keyword arguments' - key = args - if kwds: - sorted_items = sorted(kwds.items()) - key += kwd_mark - for item in sorted_items: - key += item - if typed: - key += tuple(type(v) for v in args) - if kwds: - key += tuple(type(v) for k, v in sorted_items) - elif len(key) == 1 and type(key[0]) in fasttypes: - return key[0] - return _HashedSeq(key) - - -def lru_cache(maxsize=100, typed=False): - """Least-recently-used cache decorator. - - If *maxsize* is set to None, the LRU features are disabled and the cache - can grow without bound. - - If *typed* is True, arguments of different types will be cached separately. - For example, f(3.0) and f(3) will be treated as distinct calls with - distinct results. - - Arguments to the cached function must be hashable. - - View the cache statistics named tuple (hits, misses, maxsize, currsize) with - f.cache_info(). Clear the cache and statistics with f.cache_clear(). - Access the underlying function with f.__wrapped__. - - See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used - - """ - - # Users should only access the lru_cache through its public API: - # cache_info, cache_clear, and f.__wrapped__ - # The internals of the lru_cache are encapsulated for thread safety and - # to allow the implementation to change (including a possible C version). - - def decorating_function(user_function): - - cache = dict() - stats = [0, 0] # make statistics updateable non-locally - HITS, MISSES = 0, 1 # names for the stats fields - make_key = _make_key - cache_get = cache.get # bound method to lookup key or return None - _len = len # localize the global len() function - lock = RLock() # because linkedlist updates aren't threadsafe - root = [] # root of the circular doubly linked list - root[:] = [root, root, None, None] # initialize by pointing to self - nonlocal_root = [root] # make updateable non-locally - PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields - - if maxsize == 0: - - def wrapper(*args, **kwds): - # no caching, just do a statistics update after a successful call - result = user_function(*args, **kwds) - stats[MISSES] += 1 - return result - - elif maxsize is None: - - def wrapper(*args, **kwds): - # simple caching without ordering or size limit - key = make_key(args, kwds, typed) - result = cache_get( - key, root - ) # root used here as a unique not-found sentinel - if result is not root: - stats[HITS] += 1 - return result - result = user_function(*args, **kwds) - cache[key] = result - stats[MISSES] += 1 - return result - - else: - - def wrapper(*args, **kwds): - # size limited caching that tracks accesses by recency - key = make_key(args, kwds, typed) if kwds or typed else args - with lock: - link = cache_get(key) - if link is not None: - # record recent use of the key by moving it to the front of the list - root, = nonlocal_root - link_prev, link_next, key, result = link - link_prev[NEXT] = link_next - link_next[PREV] = link_prev - last = root[PREV] - last[NEXT] = root[PREV] = link - link[PREV] = last - link[NEXT] = root - stats[HITS] += 1 - return result - result = user_function(*args, **kwds) - with lock: - root, = nonlocal_root - if key in cache: - # getting here means that this same key was added to the - # cache while the lock was released. since the link - # update is already done, we need only return the - # computed result and update the count of misses. - pass - elif _len(cache) >= maxsize: - # use the old root to store the new key and result - oldroot = root - oldroot[KEY] = key - oldroot[RESULT] = result - # empty the oldest link and make it the new root - root = nonlocal_root[0] = oldroot[NEXT] - oldkey = root[KEY] - root[KEY] = root[RESULT] = None - # now update the cache dictionary for the new links - del cache[oldkey] - cache[key] = oldroot - else: - # put result in a new link at the front of the list - last = root[PREV] - link = [last, root, key, result] - last[NEXT] = root[PREV] = cache[key] = link - stats[MISSES] += 1 - return result - - def cache_info(): - """Report cache statistics""" - with lock: - return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache)) - - def cache_clear(): - """Clear the cache and cache statistics""" - with lock: - cache.clear() - root = nonlocal_root[0] - root[:] = [root, root, None, None] - stats[:] = [0, 0] - - wrapper.__wrapped__ = user_function - wrapper.cache_info = cache_info - wrapper.cache_clear = cache_clear - return update_wrapper(wrapper, user_function) - - return decorating_function diff --git a/src/calibre/__init__.py b/src/calibre/__init__.py index 65155fb3db..b8528b743e 100644 --- a/src/calibre/__init__.py +++ b/src/calibre/__init__.py @@ -18,7 +18,7 @@ except EnvironmentError: from calibre.constants import (iswindows, isosx, islinux, isfrozen, isbsd, preferred_encoding, __appname__, __version__, __author__, - win32event, win32api, winerror, fcntl, ispy3, + win32event, win32api, winerror, fcntl, filesystem_encoding, plugins, config_dir) from calibre.startup import winutil, winutilerror from calibre.utils.icu import safe_chr @@ -446,30 +446,11 @@ class CurrentDir(object): _ncpus = None -if ispy3: - def detect_ncpus(): - global _ncpus - if _ncpus is None: - _ncpus = max(1, os.cpu_count() or 1) - return _ncpus -else: - def detect_ncpus(): - """Detects the number of effective CPUs in the system""" - global _ncpus - if _ncpus is None: - if iswindows: - import win32api - ans = win32api.GetSystemInfo()[5] - else: - import multiprocessing - ans = -1 - try: - ans = multiprocessing.cpu_count() - except Exception: - from PyQt5.Qt import QThread - ans = QThread.idealThreadCount() - _ncpus = max(1, ans) - return _ncpus +def detect_ncpus(): + global _ncpus + if _ncpus is None: + _ncpus = max(1, os.cpu_count() or 1) + return _ncpus relpath = os.path.relpath diff --git a/src/calibre/constants.py b/src/calibre/constants.py index b8dd13e8ed..190e6d2294 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -150,9 +150,7 @@ def cache_dir(): return ans -plugins_loc = sys.extensions_location -if ispy3: - plugins_loc = os.path.join(plugins_loc, '3') +plugins_loc = os.path.join(sys.extensions_location, sys.version_info.major) # plugins {{{ @@ -186,11 +184,6 @@ class Plugins(collections.Mapping): 'certgen', 'lzma_binding', ] - if not ispy3: - plugins.extend([ - 'monotonic', - 'zlib2', - ]) if iswindows: plugins.extend(['winutil', 'wpd', 'winfonts']) if isosx: diff --git a/src/calibre/customize/zipplugin.py b/src/calibre/customize/zipplugin.py index 45a664f43c..c0d528024a 100644 --- a/src/calibre/customize/zipplugin.py +++ b/src/calibre/customize/zipplugin.py @@ -11,7 +11,6 @@ from collections import OrderedDict from functools import partial from calibre import as_unicode -from calibre.constants import ispy3 from calibre.customize import (Plugin, numeric_version, platform, InvalidPlugin, PluginNotFound) from polyglot.builtins import (itervalues, map, string_or_bytes, @@ -111,8 +110,8 @@ def load_translations(namespace, zfp): from io import BytesIO trans = _translations_cache[zfp] = GNUTranslations(BytesIO(mo)) - namespace['_'] = getattr(trans, 'gettext' if ispy3 else 'ugettext') - namespace['ngettext'] = getattr(trans, 'ngettext' if ispy3 else 'ungettext') + namespace['_'] = trans.gettext + namespace['ngettext'] = trans.ngettext class PluginLoader(object): diff --git a/src/calibre/db/backend.py b/src/calibre/db/backend.py index 959bd84dbb..13b1cd14d0 100644 --- a/src/calibre/db/backend.py +++ b/src/calibre/db/backend.py @@ -16,7 +16,7 @@ from polyglot.builtins import (iteritems, itervalues, from calibre import isbytestring, force_unicode, prints, as_unicode from calibre.constants import (iswindows, filesystem_encoding, - preferred_encoding, ispy3) + preferred_encoding) from calibre.ptempfile import PersistentTemporaryFile, TemporaryFile from calibre.db import SPOOL_SIZE from calibre.db.schema_upgrades import SchemaUpgrade @@ -1752,8 +1752,6 @@ class DB(object): x = native_string_type(x) x = x.encode('utf-8') if isinstance(x, unicode_type) else x x = pickle_binary_string(x) - if not ispy3: - x = buffer(x) # noqa return x options = [(book_id, fmt.upper(), map_data(data)) for book_id, data in iteritems(options)] self.executemany('INSERT OR REPLACE INTO conversion_options(book,format,data) VALUES (?,?,?)', options) diff --git a/src/calibre/db/categories.py b/src/calibre/db/categories.py index bc972a1ba9..e268c8aa12 100644 --- a/src/calibre/db/categories.py +++ b/src/calibre/db/categories.py @@ -10,7 +10,6 @@ import copy from functools import partial from polyglot.builtins import iteritems, unicode_type, map, native_string_type -from calibre.constants import ispy3 from calibre.ebooks.metadata import author_to_author_sort from calibre.utils.config_base import tweaks from calibre.utils.icu import sort_key, collation_order @@ -47,15 +46,8 @@ class Tag(object): def string_representation(self): return u'%s:%s:%s:%s:%s'%(self.name, self.count, self.id, self.state, self.category) - if ispy3: - def __str__(self): - return self.string_representation - else: - def __str__(self): - return self.string_representation.encode('utf-8') - - def __unicode__(self): - return self.string_representation + def __str__(self): + return self.string_representation def __repr__(self): return native_string_type(self) diff --git a/src/calibre/db/cli/main.py b/src/calibre/db/cli/main.py index f78be8b3f9..d3380341c7 100644 --- a/src/calibre/db/cli/main.py +++ b/src/calibre/db/cli/main.py @@ -2,8 +2,6 @@ # vim:fileencoding=utf-8 # License: GPLv3 Copyright: 2017, Kovid Goyal - - import json import os import sys @@ -125,7 +123,7 @@ def read_credentials(opts): pw = opts.password if pw: if pw == '': - from calibre.utils.unicode_getpass import getpass + from getpass import getpass pw = getpass(_('Enter the password: ')) elif pw.startswith(''): with lopen(pw[3:-1], 'rb') as f: diff --git a/src/calibre/devices/kobo/bookmark.py b/src/calibre/devices/kobo/bookmark.py index 8de1b66944..cd354adb6f 100644 --- a/src/calibre/devices/kobo/bookmark.py +++ b/src/calibre/devices/kobo/bookmark.py @@ -7,7 +7,6 @@ __docformat__ = 'restructuredtext en' import os -from calibre.constants import ispy3 from polyglot.builtins import unicode_type from calibre.devices.usbms.driver import debug_print @@ -190,7 +189,6 @@ class Bookmark(): # {{{ return ans - if ispy3: - __str__ = __unicode__ + __str__ = __unicode__ # }}} diff --git a/src/calibre/devices/kobo/books.py b/src/calibre/devices/kobo/books.py index 9a8d06798c..d93939c424 100644 --- a/src/calibre/devices/kobo/books.py +++ b/src/calibre/devices/kobo/books.py @@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en' import os, time, sys from functools import cmp_to_key -from calibre.constants import preferred_encoding, DEBUG, ispy3 +from calibre.constants import preferred_encoding, DEBUG from calibre import isbytestring from calibre.ebooks.metadata.book.base import Metadata @@ -118,8 +118,7 @@ class Book(Book_): return super(Book,self).__unicode__() + u"\n" + ans - if ispy3: - __str__ = __unicode__ + __str__ = __unicode__ class ImageWrapper(object): diff --git a/src/calibre/devices/mtp/unix/driver.py b/src/calibre/devices/mtp/unix/driver.py index bb5b235e8a..7c8d8a365d 100644 --- a/src/calibre/devices/mtp/unix/driver.py +++ b/src/calibre/devices/mtp/unix/driver.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai - __license__ = 'GPL v3' __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' @@ -12,11 +11,11 @@ from collections import namedtuple from functools import partial from calibre import prints, as_unicode, force_unicode -from calibre.constants import plugins, islinux, isosx, ispy3 +from calibre.constants import plugins, islinux, isosx from calibre.ptempfile import SpooledTemporaryFile from calibre.devices.errors import OpenFailed, DeviceError, BlacklistedDevice, OpenActionNeeded from calibre.devices.mtp.base import MTPDeviceBase, synchronous, debug -from polyglot.builtins import unicode_type, as_bytes +from polyglot.builtins import unicode_type MTPDevice = namedtuple('MTPDevice', 'busnum devnum vendor_id product_id ' 'bcd serial manufacturer product') @@ -167,9 +166,8 @@ class MTP_DEVICE(MTPDeviceBase): def create_device(self, connected_device): d = connected_device man, prod = d.manufacturer, d.product - if ispy3: - man = force_unicode(man, 'utf-8') if isinstance(man, bytes) else man - prod = force_unicode(prod, 'utf-8') if isinstance(prod, bytes) else prod + man = force_unicode(man, 'utf-8') if isinstance(man, bytes) else man + prod = force_unicode(prod, 'utf-8') if isinstance(prod, bytes) else prod return self.libmtp.Device(d.busnum, d.devnum, d.vendor_id, d.product_id, man, prod, d.serial) @@ -399,9 +397,8 @@ class MTP_DEVICE(MTPDeviceBase): sid, pid = parent.storage_id, parent.object_id if pid == sid: pid = 0xFFFFFFFF - ename = name if ispy3 else as_bytes(name) - ans, errs = self.dev.put_file(sid, pid, ename, stream, size, callback) + ans, errs = self.dev.put_file(sid, pid, name, stream, size, callback) if ans is None: raise DeviceError('Failed to upload file named: %s to %s: %s' %(name, parent.full_path, self.format_errorstack(errs))) diff --git a/src/calibre/ebooks/compression/tcr.py b/src/calibre/ebooks/compression/tcr.py index 453e14395a..5aaab1052d 100644 --- a/src/calibre/ebooks/compression/tcr.py +++ b/src/calibre/ebooks/compression/tcr.py @@ -6,7 +6,7 @@ __copyright__ = '2009, John Schember ' __docformat__ = 'restructuredtext en' import re -from polyglot.builtins import int_to_byte, is_py3, range +from polyglot.builtins import int_to_byte, range class TCRCompressor(object): @@ -42,8 +42,6 @@ class TCRCompressor(object): possible_codes.append(single_code.pop()) for code in possible_codes: - if not is_py3: - code = bytearray(code) self.coded_txt = self.coded_txt.replace(code, code[0:1]) self.codes[code[0]] = b'%s%s' % (self.codes[code[0]], self.codes[code[1]]) diff --git a/src/calibre/ebooks/html/input.py b/src/calibre/ebooks/html/input.py index 8e85b23dde..ae9511b81f 100644 --- a/src/calibre/ebooks/html/input.py +++ b/src/calibre/ebooks/html/input.py @@ -17,7 +17,7 @@ from calibre.ebooks.oeb.base import urlunquote from calibre.ebooks.chardet import detect_xml_encoding from calibre.constants import iswindows from calibre import unicode_path, as_unicode, replace_entities -from polyglot.builtins import is_py3, unicode_type +from polyglot.builtins import unicode_type from polyglot.urllib import urlparse, urlunparse @@ -67,9 +67,6 @@ class Link(object): def __str__(self): return 'Link: %s --> %s'%(self.url, self.path) - if not is_py3: - __unicode__ = __str__ - class IgnoreFile(Exception): diff --git a/src/calibre/ebooks/lit/reader.py b/src/calibre/ebooks/lit/reader.py index 367daf3042..73a2e5035e 100644 --- a/src/calibre/ebooks/lit/reader.py +++ b/src/calibre/ebooks/lit/reader.py @@ -17,7 +17,6 @@ import calibre.ebooks.lit.mssha1 as mssha1 from calibre.ebooks.oeb.base import urlnormalize, xpath from calibre.ebooks.oeb.reader import OEBReader from calibre.ebooks import DRMError -from calibre.constants import ispy3 from calibre import plugins from polyglot.builtins import codepoint_to_chr, unicode_type, string_or_bytes, range, itervalues from polyglot.urllib import unquote as urlunquote, urldefrag @@ -185,7 +184,7 @@ class UnBinary(object): return self.unicode_representation def __str__(self): - return self.unicode_representation if ispy3 else self.binary_representation + return self.unicode_representation def binary_to_text(self, bin, buf): stack = [(0, None, None, 0, 0, False, False, 'text', 0)] diff --git a/src/calibre/ebooks/lrf/objects.py b/src/calibre/ebooks/lrf/objects.py index 1d9de79a3a..5382529bb3 100644 --- a/src/calibre/ebooks/lrf/objects.py +++ b/src/calibre/ebooks/lrf/objects.py @@ -7,7 +7,7 @@ import struct, array, zlib, io, collections, re from calibre.ebooks.lrf import LRFParseError, PRS500_PROFILE from calibre import entity_to_unicode, prepare_string_for_xml from calibre.ebooks.lrf.tags import Tag -from polyglot.builtins import is_py3, unicode_type +from polyglot.builtins import unicode_type ruby_tags = { 0xF575: ['rubyAlignAndAdjust', 'W'], @@ -208,9 +208,6 @@ class StyleObject(object): s += '/>\n' return s - if not is_py3: - __unicode__ = __str__ - def as_dict(self): d = {} for h in self.tag_map.values(): @@ -256,9 +253,6 @@ class Color(object): def __str__(self): return '0x%02x%02x%02x%02x'%(self.a, self.r, self.g, self.b) - if not is_py3: - __unicode__ = __str__ - def __len__(self): return 4 @@ -289,9 +283,6 @@ class PageDiv(EmptyPageElement): return '\n\n'%\ (self.pain, self.spacesize, self.linewidth, self.color) - if not is_py3: - __unicode__ = __str__ - class RuledLine(EmptyPageElement): @@ -307,9 +298,6 @@ class RuledLine(EmptyPageElement): return '\n\n'%\ (self.linelength, self.linetype, self.linewidth, self.linecolor) - if not is_py3: - __unicode__ = __str__ - class Wait(EmptyPageElement): @@ -319,9 +307,6 @@ class Wait(EmptyPageElement): def __str__(self): return '\n\n'%(self.time) - if not is_py3: - __unicode__ = __str__ - class Locate(EmptyPageElement): @@ -333,9 +318,6 @@ class Locate(EmptyPageElement): def __str__(self): return '\n\n'%(self.pos) - if not is_py3: - __unicode__ = __str__ - class BlockSpace(EmptyPageElement): @@ -346,9 +328,6 @@ class BlockSpace(EmptyPageElement): return '\n\n'%\ (self.xspace, self.yspace) - if not is_py3: - __unicode__ = __str__ - class Page(LRFStream): tag_map = { @@ -450,9 +429,6 @@ class Page(LRFStream): s += '\n\n' return s - if not is_py3: - __unicode__ = __str__ - def to_html(self): s = '' for i in self: @@ -641,9 +617,6 @@ class Block(LRFStream, TextCSS): return s return s.rstrip() + ' />\n' - if not is_py3: - __unicode__ = __str__ - def to_html(self): if self.name == 'TextBlock': return '
%s
'%(self.style_id, self.textstyle_id, self.content.to_html()) @@ -722,9 +695,6 @@ class Text(LRFStream): s += '%s="%s" '%(name, val) return s.rstrip() + (' />' if self.self_closing else '>') - if not is_py3: - __unicode__ = __str__ - def to_html(self): s = '' return s @@ -922,9 +892,6 @@ class Text(LRFStream): raise LRFParseError('Malformed text stream %s'%([i.name for i in open_containers if isinstance(i, Text.TextTag)],)) return s - if not is_py3: - __unicode__ = __str__ - def to_html(self): s = '' open_containers = collections.deque() @@ -971,9 +938,6 @@ class Image(LRFObject): return '\n'%\ (self.id, self.x0, self.y0, self.x1, self.y1, self.xsize, self.ysize, self.refstream) - if not is_py3: - __unicode__ = __str__ - class PutObj(EmptyPageElement): @@ -984,9 +948,6 @@ class PutObj(EmptyPageElement): def __str__(self): return ''%(self.x1, self.y1, self.refobj) - if not is_py3: - __unicode__ = __str__ - class Canvas(LRFStream): tag_map = { @@ -1035,9 +996,6 @@ class Canvas(LRFStream): s += '\n'%(self.__class__.__name__,) return s - if not is_py3: - __unicode__ = __str__ - def __iter__(self): for i in self._contents: yield i @@ -1075,9 +1033,6 @@ class ImageStream(LRFStream): return '\n'%\ (self.id, self.encoding, self.file) - if not is_py3: - __unicode__ = __str__ - class Import(LRFStream): pass @@ -1167,9 +1122,6 @@ class Button(LRFObject): s += '\n' return s - if not is_py3: - __unicode__ = __str__ - refpage = property(fget=lambda self : self.jump_action(2)[0]) refobj = property(fget=lambda self : self.jump_action(2)[1]) @@ -1241,9 +1193,6 @@ class BookAttr(StyleObject, LRFObject): s += '\n' return s - if not is_py3: - __unicode__ = __str__ - class SimpleText(Text): pass @@ -1257,9 +1206,6 @@ class TocLabel(object): def __str__(self): return '%s\n'%(self.refpage, self.refobject, self.label) - if not is_py3: - __unicode__ = __str__ - class TOCObject(LRFStream): @@ -1287,9 +1233,6 @@ class TOCObject(LRFStream): s += unicode_type(i) return s + '\n' - if not is_py3: - __unicode__ = __str__ - object_map = [ None, # 00 diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index a33e2ad91b..2aa0b082b1 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en' import copy, traceback from calibre import prints -from calibre.constants import DEBUG, ispy3 +from calibre.constants import DEBUG from calibre.ebooks.metadata.book import (SC_COPYABLE_FIELDS, SC_FIELDS_COPY_NOT_NULL, STANDARD_METADATA_FIELDS, TOP_LEVEL_IDENTIFIERS, ALL_METADATA_FIELDS) @@ -810,13 +810,7 @@ class Metadata(object): ans[i] = '%s%s'%x return '%s
'%'\n'.join(ans) - if ispy3: - __str__ = __unicode__representation__ - else: - __unicode__ = __unicode__representation__ - - def __str__(self): - return self.__unicode__().encode('utf-8') + __str__ = __unicode__representation__ def __nonzero__(self): return bool(self.title or self.author or self.comments or self.tags) diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index 792cb1c550..4876196882 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -13,7 +13,7 @@ import re, sys, unittest, functools, os, uuid, glob, io, json, copy from lxml import etree from calibre.ebooks import escape_xpath_attr -from calibre.constants import __appname__, __version__, filesystem_encoding, ispy3 +from calibre.constants import __appname__, __version__, filesystem_encoding from calibre.ebooks.metadata.toc import TOC from calibre.ebooks.metadata.utils import parse_opf, pretty_print_opf as _pretty_print from calibre.ebooks.metadata import string_to_authors, MetaInformation, check_isbn @@ -205,13 +205,7 @@ class ManifestItem(Resource): # {{{ def __unicode__representation__(self): return u''%(self.id, self.href(), self.media_type) - if ispy3: - __str__ = __unicode__representation__ - else: - __unicode__ = __unicode__representation__ - - def __str__(self): - return unicode_type(self).encode('utf-8') + __str__ = __unicode__representation__ def __repr__(self): return unicode_type(self) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index acc7876dcd..957ded7bd1 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -1,4 +1,3 @@ - ''' Basic support for manipulating OEB 1.x/2.0 content and metadata. ''' @@ -14,7 +13,7 @@ from operator import attrgetter from lxml import etree, html from calibre import force_unicode -from calibre.constants import filesystem_encoding, __version__, ispy3 +from calibre.constants import filesystem_encoding, __version__ from calibre.translations.dynamic import translate from calibre.utils.xml_parse import safe_xml_fromstring from calibre.ebooks.chardet import xml_to_unicode @@ -756,15 +755,8 @@ class Metadata(object): return 'Item(term=%r, value=%r, attrib=%r)' \ % (barename(self.term), self.value, self.attrib) - if ispy3: - def __str__(self): - return as_unicode(self.value) - else: - def __str__(self): - return unicode_type(self.value).encode('ascii', 'xmlcharrefreplace') - - def __unicode__(self): - return as_unicode(self.value) + def __str__(self): + return as_unicode(self.value) def to_opf1(self, dcmeta=None, xmeta=None, nsrmap={}): attrib = {} @@ -1099,15 +1091,8 @@ class Manifest(object): def bytes_representation(self): return serialize(self.data, self.media_type, pretty_print=self.oeb.pretty_print) - if ispy3: - def __str__(self): - return self.unicode_representation - else: - def __unicode__(self): - return self.unicode_representation - - def __str__(self): - return self.bytes_representation + def __str__(self): + return self.unicode_representation def __eq__(self, other): return self is other @@ -1617,15 +1602,8 @@ class TOC(object): ans.extend(child.get_lines(lvl+1)) return ans - if ispy3: - def __str__(self): - return '\n'.join(self.get_lines()) - else: - def __unicode__(self): - return '\n'.join(self.get_lines()) - - def __str__(self): - return b'\n'.join([x.encode('utf-8') for x in self.get_lines()]) + def __str__(self): + return '\n'.join(self.get_lines()) def to_opf1(self, tour): for node in self.nodes: diff --git a/src/calibre/ebooks/pdf/pdftohtml.py b/src/calibre/ebooks/pdf/pdftohtml.py index 9eedf39a9c..80287e9a59 100644 --- a/src/calibre/ebooks/pdf/pdftohtml.py +++ b/src/calibre/ebooks/pdf/pdftohtml.py @@ -2,8 +2,6 @@ # vim:fileencoding=utf-8 # License: GPLv3 Copyright: 2008, Kovid Goyal - - import errno import os import re @@ -11,23 +9,18 @@ import shutil import subprocess import sys -from calibre import CurrentDir, xml_replace_entities, prints -from calibre.constants import ( - filesystem_encoding, isbsd, islinux, isosx, ispy3, iswindows -) +from calibre import CurrentDir, prints, xml_replace_entities +from calibre.constants import isbsd, islinux, isosx, iswindows from calibre.ebooks import ConversionError, DRMError from calibre.ebooks.chardet import xml_to_unicode from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.cleantext import clean_xml_chars from calibre.utils.ipc import eintr_retry_call - PDFTOHTML = 'pdftohtml' def popen(cmd, **kw): - if not ispy3: - cmd = [x.encode(filesystem_encoding) if not isinstance(x, bytes) else x for x in cmd] if iswindows: kw['creationflags'] = 0x08 return subprocess.Popen(cmd, **kw) diff --git a/src/calibre/ebooks/pdf/render/common.py b/src/calibre/ebooks/pdf/render/common.py index 0c607fd947..8fe22e4476 100644 --- a/src/calibre/ebooks/pdf/render/common.py +++ b/src/calibre/ebooks/pdf/render/common.py @@ -10,7 +10,7 @@ import codecs, zlib, numbers from io import BytesIO from datetime import datetime -from calibre.constants import plugins, ispy3 +from calibre.constants import plugins from calibre.utils.logging import default_log from polyglot.builtins import iteritems, unicode_type, codepoint_to_chr from polyglot.binary import as_hex_bytes @@ -69,7 +69,7 @@ def serialize(o, stream): # Must check bool before int as bools are subclasses of int stream.write_raw(b'true' if o else b'false') elif isinstance(o, numbers.Integral): - stream.write_raw(unicode_type(o).encode('ascii') if ispy3 else bytes(o)) + stream.write_raw(unicode_type(o).encode('ascii')) elif hasattr(o, 'pdf_serialize'): o.pdf_serialize(stream) elif o is None: diff --git a/src/calibre/gui2/linux_file_dialogs.py b/src/calibre/gui2/linux_file_dialogs.py index 89bb295a76..6e4f15f7ad 100644 --- a/src/calibre/gui2/linux_file_dialogs.py +++ b/src/calibre/gui2/linux_file_dialogs.py @@ -2,8 +2,6 @@ # vim:fileencoding=utf-8 # License: GPLv3 Copyright: 2017, Kovid Goyal - - import functools import os import subprocess @@ -14,7 +12,7 @@ from threading import Thread from PyQt5.Qt import QEventLoop from calibre import force_unicode -from calibre.constants import DEBUG, filesystem_encoding, ispy3, preferred_encoding +from calibre.constants import DEBUG, filesystem_encoding, preferred_encoding from calibre.utils.config import dynamic from polyglot.builtins import getenv, reraise, string_or_bytes, unicode_type @@ -107,14 +105,13 @@ def decode_output(raw): def run(cmd): from calibre.gui2 import sanitize_env_vars - ecmd = cmd if ispy3 else list(map(encode_arg, cmd)) if DEBUG: try: - print(ecmd) + print(cmd) except Exception: pass with sanitize_env_vars(): - p = subprocess.Popen(ecmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() ret = p.wait() return ret, decode_output(stdout), decode_output(stderr) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 653f5f83c5..2f10cd3690 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -25,7 +25,7 @@ from calibre.library.custom_columns import CustomColumns from calibre.library.sqlite import connect, IntegrityError from calibre.library.prefs import DBPrefs from calibre.ebooks.metadata.book.base import Metadata -from calibre.constants import preferred_encoding, iswindows, filesystem_encoding, ispy3 +from calibre.constants import preferred_encoding, iswindows, filesystem_encoding from calibre.ptempfile import (PersistentTemporaryFile, base_dir, SpooledTemporaryFile) from calibre.customize.ui import (run_plugins_on_import, @@ -1752,10 +1752,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): return 'n=%s s=%s c=%d rt=%d rc=%d id=%s' % ( self.n, self.s, self.c, self.rt, self.rc, self.id) - if ispy3: - __str__ = __unicode_representation__ - else: - __str__ = __unicode__ = __unicode_representation__ + __str__ = __unicode_representation__ def clean_user_categories(self): user_cats = self.prefs.get('user_categories', {}) diff --git a/src/calibre/linux.py b/src/calibre/linux.py index 0e7d96692a..7f6dc6036f 100644 --- a/src/calibre/linux.py +++ b/src/calibre/linux.py @@ -2,8 +2,6 @@ # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai # License: GPLv3 Copyright: 2008, Kovid Goyal - - ''' Post installation script for linux ''' import sys, os, textwrap, stat, errno @@ -11,7 +9,7 @@ from subprocess import check_call, check_output from functools import partial from calibre import __appname__, prints, guess_type -from calibre.constants import islinux, isbsd, ispy3 +from calibre.constants import islinux, isbsd from calibre.customize.ui import all_input_formats from calibre.ptempfile import TemporaryDirectory from calibre import CurrentDir @@ -1191,7 +1189,7 @@ def write_appdata(key, entry, base, translators): for para in entry['description']: description.append(E.p(para)) for lang, t in iteritems(translators): - tp = getattr(t, 'gettext' if ispy3 else 'ugettext')(para) + tp = t.gettext(para) if tp != para: description.append(E.p(tp)) description[-1].set('{http://www.w3.org/XML/1998/namespace}lang', lang) @@ -1208,7 +1206,7 @@ def write_appdata(key, entry, base, translators): type='desktop' ) for lang, t in iteritems(translators): - tp = getattr(t, 'gettext' if ispy3 else 'ugettext')(entry['summary']) + tp = t.gettext(entry['summary']) if tp != entry['summary']: root.append(E.summary(tp)) root[-1].set('{http://www.w3.org/XML/1998/namespace}lang', lang) diff --git a/src/calibre/ptempfile.py b/src/calibre/ptempfile.py index 762b5d6225..63de86a52a 100644 --- a/src/calibre/ptempfile.py +++ b/src/calibre/ptempfile.py @@ -9,7 +9,7 @@ import tempfile, os, atexit from polyglot.builtins import map, getenv from calibre.constants import (__version__, __appname__, filesystem_encoding, - iswindows, get_windows_temp_path, isosx, ispy3) + iswindows, get_windows_temp_path, isosx) def cleanup(path): @@ -265,23 +265,17 @@ class SpooledTemporaryFile(tempfile.SpooledTemporaryFile): suffix = '' if dir is None: dir = base_dir() - if ispy3: - self._name = None - tempfile.SpooledTemporaryFile.__init__(self, max_size=max_size, - suffix=suffix, prefix=prefix, dir=dir, mode=mode) - else: - tempfile.SpooledTemporaryFile.__init__(self, max_size=max_size, - suffix=suffix, prefix=prefix, dir=dir, mode=mode, - bufsize=bufsize) + self._name = None + tempfile.SpooledTemporaryFile.__init__(self, max_size=max_size, + suffix=suffix, prefix=prefix, dir=dir, mode=mode) - if ispy3: - @property - def name(self): - return self._name + @property + def name(self): + return self._name - @name.setter - def name(self, val): - self._name = val + @name.setter + def name(self, val): + self._name = val def truncate(self, *args): # The stdlib SpooledTemporaryFile implementation of truncate() doesn't diff --git a/src/calibre/spell/dictionary.py b/src/calibre/spell/dictionary.py index 52a23c03fe..2f9ed3e1e6 100644 --- a/src/calibre/spell/dictionary.py +++ b/src/calibre/spell/dictionary.py @@ -14,7 +14,7 @@ from itertools import chain from calibre import prints from calibre.constants import ( - config_dir, filesystem_encoding, ispy3, iswindows, plugins + config_dir, filesystem_encoding, iswindows, plugins ) from calibre.spell import parse_lang_code from calibre.utils.config import JSONConfig @@ -174,8 +174,6 @@ def load_dictionary(dictionary): path = os.path.abspath(path) if iswindows: path = r'\\?\{}'.format(path) - if not ispy3: - path = path.encode('utf-8') return path obj = hunspell.Dictionary(fix_path(dictionary.dicpath), fix_path(dictionary.affpath)) diff --git a/src/calibre/srv/http_response.py b/src/calibre/srv/http_response.py index 1fd74e3c73..737858cf0e 100644 --- a/src/calibre/srv/http_response.py +++ b/src/calibre/srv/http_response.py @@ -12,10 +12,10 @@ from itertools import chain, repeat from operator import itemgetter from functools import wraps -from polyglot.builtins import iteritems, itervalues, reraise, map, is_py3, unicode_type, string_or_bytes +from polyglot.builtins import iteritems, itervalues, reraise, map, unicode_type, string_or_bytes from calibre import guess_type, force_unicode -from calibre.constants import __version__, plugins, ispy3 +from calibre.constants import __version__ from calibre.srv.loop import WRITE from calibre.srv.errors import HTTPSimpleResponse from calibre.srv.http_request import HTTPRequest, read_headers @@ -33,15 +33,8 @@ MULTIPART_SEPARATOR = uuid.uuid4().hex if isinstance(MULTIPART_SEPARATOR, bytes): MULTIPART_SEPARATOR = MULTIPART_SEPARATOR.decode('ascii') COMPRESSIBLE_TYPES = {'application/json', 'application/javascript', 'application/xml', 'application/oebps-package+xml'} -if is_py3: - import zlib - from itertools import zip_longest -else: - zlib, zlib2_err = plugins['zlib2'] - if zlib2_err: - raise RuntimeError('Failed to load the zlib2 module with error: ' + zlib2_err) - del zlib2_err - from itertools import izip_longest as zip_longest +import zlib +from itertools import zip_longest def header_list_to_file(buf): # {{{ @@ -290,8 +283,8 @@ class RequestData(object): # {{{ if lang_code != self.lang_code: found, lang, t = self.get_translator(lang_code) self.lang_code = lang - self.gettext_func = getattr(t, 'gettext' if ispy3 else 'ugettext') - self.ngettext_func = getattr(t, 'ngettext' if ispy3 else 'ungettext') + self.gettext_func = t.gettext + self.ngettext_func = t.ngettext # }}} diff --git a/src/calibre/srv/manage_users_cli.py b/src/calibre/srv/manage_users_cli.py index a6ee4f6b94..bbbf3aad67 100644 --- a/src/calibre/srv/manage_users_cli.py +++ b/src/calibre/srv/manage_users_cli.py @@ -2,8 +2,6 @@ # vim:fileencoding=utf-8 # License: GPLv3 Copyright: 2017, Kovid Goyal - - import sys from functools import partial @@ -77,7 +75,7 @@ def manage_users_cli(path=None): return get_valid(_('Enter the username'), validate) def get_pass(username): - from calibre.utils.unicode_getpass import getpass + from getpass import getpass while True: one = getpass( diff --git a/src/calibre/srv/opts.py b/src/calibre/srv/opts.py index e1a69d1a10..68f4a131aa 100644 --- a/src/calibre/srv/opts.py +++ b/src/calibre/srv/opts.py @@ -12,11 +12,8 @@ from functools import partial from calibre.constants import config_dir from calibre.utils.lock import ExclusiveFile -from polyglot.builtins import itervalues, is_py3 -if is_py3: - from itertools import zip_longest -else: - from itertools import izip_longest as zip_longest +from polyglot.builtins import itervalues +from itertools import zip_longest Option = namedtuple('Option', 'name default longdoc shortdoc choices') diff --git a/src/calibre/srv/routes.py b/src/calibre/srv/routes.py index 62ef365a78..523c9780a1 100644 --- a/src/calibre/srv/routes.py +++ b/src/calibre/srv/routes.py @@ -8,7 +8,6 @@ __copyright__ = '2015, Kovid Goyal ' import sys, inspect, re, time, numbers, json as jsonlib, textwrap from operator import attrgetter -from calibre.constants import ispy3 from calibre.srv.errors import HTTPSimpleResponse, HTTPNotFound, RouteError from calibre.srv.utils import http_date from calibre.utils.serialize import msgpack_dumps, json_dumps, MSGPACK_MIME @@ -88,7 +87,7 @@ def endpoint(route, f.ok_code = ok_code f.is_endpoint = True f.needs_db_write = needs_db_write - argspec = inspect.getfullargspec(f) if ispy3 else inspect.getargspec(f) + argspec = inspect.getfullargspec(f) if len(argspec.args) < 2: raise TypeError('The endpoint %r must take at least two arguments' % f.route) f.__annotations__ = { @@ -158,10 +157,7 @@ class Route(object): self.names = [n for n, m in matchers if n is not None] self.all_names = frozenset(self.names) self.required_names = self.all_names - frozenset(self.defaults) - if ispy3: - argspec = inspect.getfullargspec(self.endpoint) - else: - argspec = inspect.getargspec(self.endpoint) + argspec = inspect.getfullargspec(self.endpoint) if len(self.names) + 2 != len(argspec.args) - len(argspec.defaults or ()): raise route_error('Function must take %d non-default arguments' % (len(self.names) + 2)) if argspec.args[2:len(self.names)+2] != self.names: diff --git a/src/calibre/srv/tests/base.py b/src/calibre/srv/tests/base.py index 325c5c8456..de2176bcb6 100644 --- a/src/calibre/srv/tests/base.py +++ b/src/calibre/srv/tests/base.py @@ -12,7 +12,6 @@ from functools import partial from threading import Thread from calibre.srv.utils import ServerLog -from calibre.constants import ispy3 from polyglot import http_client rmtree = partial(shutil.rmtree, ignore_errors=True) @@ -121,10 +120,7 @@ class TestServer(Thread): timeout = self.loop.opts.timeout if interface is None: interface = self.address[0] - if ispy3: - return http_client.HTTPConnection(interface, self.address[1], timeout=timeout) - else: - return http_client.HTTPConnection(interface, self.address[1], strict=True, timeout=timeout) + return http_client.HTTPConnection(interface, self.address[1], timeout=timeout) def change_handler(self, handler): from calibre.srv.http_response import create_http_handler diff --git a/src/calibre/srv/tests/http.py b/src/calibre/srv/tests/http.py index e32f7dbcc7..81f91b483e 100644 --- a/src/calibre/srv/tests/http.py +++ b/src/calibre/srv/tests/http.py @@ -10,7 +10,6 @@ from io import BytesIO from tempfile import NamedTemporaryFile from calibre import guess_type -from calibre.constants import ispy3 from calibre.srv.tests.base import BaseTest, TestServer from calibre.srv.utils import eintr_retry_call from calibre.utils.monotonic import monotonic @@ -96,7 +95,7 @@ class TestHTTP(BaseTest): conn.request('GET', '/', headers={'Accept-Language': al}) r = conn.getresponse() self.ae(r.status, http_client.OK) - q += getattr(get_translator(q)[-1], 'gettext' if ispy3 else 'ugettext')('Unknown') + q += get_translator(q)[-1].gettext('Unknown') self.ae(r.read(), q.encode('utf-8')) test('en', 'en') diff --git a/src/calibre/srv/tests/loop.py b/src/calibre/srv/tests/loop.py index 8b25e7b448..1710bd2925 100644 --- a/src/calibre/srv/tests/loop.py +++ b/src/calibre/srv/tests/loop.py @@ -11,7 +11,6 @@ from unittest import skipIf from glob import glob from threading import Event -from calibre.constants import ispy3 from calibre.srv.pre_activated import has_preactivated_support from calibre.srv.tests.base import BaseTest, TestServer from calibre.ptempfile import TemporaryDirectory @@ -114,10 +113,7 @@ class LoopTest(BaseTest): def test_bonjour(self): 'Test advertising via BonJour' from calibre.srv.bonjour import BonJour - if ispy3: - from zeroconf import Zeroconf - else: - from calibre.utils.Zeroconf import Zeroconf + from zeroconf import Zeroconf b = BonJour(wait_for_stop=False) with TestServer(lambda data:(data.path[0] + data.read()), plugins=(b,), shutdown_timeout=5) as server: self.assertTrue(b.started.wait(5), 'BonJour not started') diff --git a/src/calibre/srv/utils.py b/src/calibre/srv/utils.py index 0706287a2e..50bd480535 100644 --- a/src/calibre/srv/utils.py +++ b/src/calibre/srv/utils.py @@ -11,7 +11,7 @@ from email.utils import formatdate from operator import itemgetter from calibre import prints -from calibre.constants import iswindows, ispy3 +from calibre.constants import iswindows from calibre.srv.errors import HTTPNotFound from calibre.utils.localization import get_translator from calibre.utils.socket_inheritance import set_socket_inherit @@ -47,8 +47,7 @@ class MultiDict(dict): # {{{ @staticmethod def create_from_query_string(qs): ans = MultiDict() - if ispy3: - qs = as_unicode(qs) + qs = as_unicode(qs) for k, v in iteritems(parse_qs(qs, keep_blank_values=True)): dict.__setitem__(ans, as_unicode(k), [as_unicode(x) for x in v]) return ans @@ -59,7 +58,7 @@ class MultiDict(dict): # {{{ self[key] = val def items(self, duplicates=True): - f = dict.items if ispy3 else dict.iteritems + f = dict.items for k, v in f(self): if duplicates: for x in v: @@ -69,7 +68,7 @@ class MultiDict(dict): # {{{ iteritems = items def values(self, duplicates=True): - f = dict.values if ispy3 else dict.itervalues + f = dict.values for v in f(self): if duplicates: for x in v: @@ -291,8 +290,6 @@ def encode_path(*components): class Cookie(SimpleCookie): def _BaseCookie__set(self, key, real_value, coded_value): - if not ispy3 and not isinstance(key, bytes): - key = key.encode('ascii') # Python 2.x cannot handle unicode keys return SimpleCookie._BaseCookie__set(self, key, real_value, coded_value) @@ -315,14 +312,11 @@ class RotatingStream(object): self.set_output() def set_output(self): - if ispy3: - if iswindows: - self.stream = share_open(self.filename, 'ab') - else: - # see https://bugs.python.org/issue27805 - self.stream = open(os.open(self.filename, os.O_WRONLY|os.O_APPEND|os.O_CREAT|os.O_CLOEXEC), 'wb') + if iswindows: + self.stream = share_open(self.filename, 'ab') else: - self.stream = share_open(self.filename, 'ab', -1 if iswindows else 1) # line buffered + # see https://bugs.python.org/issue27805 + self.stream = open(os.open(self.filename, os.O_WRONLY|os.O_APPEND|os.O_CREAT|os.O_CLOEXEC), 'wb') try: self.current_pos = self.stream.tell() except EnvironmentError: @@ -337,14 +331,12 @@ class RotatingStream(object): kwargs['safe_encode'] = True kwargs['file'] = self.stream self.current_pos += prints(*args, **kwargs) - if iswindows or ispy3: - # For some reason line buffering does not work on windows - # and in python 3 it only works with text mode streams - end = kwargs.get('end', b'\n') - if isinstance(end, unicode_type): - end = end.encode('utf-8') - if b'\n' in end: - self.flush() + # line bufferring only works with text mode streams + end = kwargs.get('end', b'\n') + if isinstance(end, unicode_type): + end = end.encode('utf-8') + if b'\n' in end: + self.flush() self.rollover() def rename(self, src, dest): diff --git a/src/calibre/startup.py b/src/calibre/startup.py index a20f7cb124..924d6ebf26 100644 --- a/src/calibre/startup.py +++ b/src/calibre/startup.py @@ -10,7 +10,7 @@ Perform various initialization tasks. import locale, sys # Default translation is NOOP -from polyglot.builtins import builtins, is_py3, unicode_type +from polyglot.builtins import builtins, unicode_type builtins.__dict__['_'] = lambda s: s # For strings which belong in the translation tables, but which shouldn't be @@ -116,44 +116,7 @@ if not _run_once: pass # local_open() opens a file that wont be inherited by child processes - if is_py3: - local_open = open # PEP 446 - elif iswindows: - def local_open(name, mode='r', bufsize=-1): - mode += 'N' - return open(name, mode, bufsize) - elif isosx: - import fcntl - FIOCLEX = 0x20006601 - - def local_open(name, mode='r', bufsize=-1): - ans = open(name, mode, bufsize) - try: - fcntl.ioctl(ans.fileno(), FIOCLEX) - except EnvironmentError: - fcntl.fcntl(ans, fcntl.F_SETFD, fcntl.fcntl(ans, fcntl.F_GETFD) | fcntl.FD_CLOEXEC) - return ans - else: - import fcntl - try: - cloexec_flag = fcntl.FD_CLOEXEC - except AttributeError: - cloexec_flag = 1 - supports_mode_e = False - - def local_open(name, mode='r', bufsize=-1): - global supports_mode_e - mode += 'e' - ans = open(name, mode, bufsize) - if supports_mode_e: - return ans - old = fcntl.fcntl(ans, fcntl.F_GETFD) - if not (old & cloexec_flag): - fcntl.fcntl(ans, fcntl.F_SETFD, old | cloexec_flag) - else: - supports_mode_e = True - return ans - + local_open = open # PEP 446 builtins.__dict__['lopen'] = local_open from calibre.utils.icu import title_case, lower as icu_lower, upper as icu_upper diff --git a/src/calibre/test_build.py b/src/calibre/test_build.py index 71885930c0..a8e532d7f2 100644 --- a/src/calibre/test_build.py +++ b/src/calibre/test_build.py @@ -12,7 +12,7 @@ Test a binary calibre build to ensure that all needed binary images/libraries ha import os, ctypes, sys, unittest, time -from calibre.constants import plugins, iswindows, islinux, isosx, ispy3, plugins_loc +from calibre.constants import plugins, iswindows, islinux, isosx, plugins_loc from polyglot.builtins import iteritems, map, unicode_type, getenv, native_string_type is_ci = os.environ.get('CI', '').lower() == 'true' @@ -81,11 +81,7 @@ class BuildTest(unittest.TestCase): del soupsieve, bs4 def test_zeroconf(self): - if ispy3: - import zeroconf as z, ifaddr - else: - import calibre.utils.Zeroconf as z - ifaddr = None + import zeroconf as z, ifaddr del z del ifaddr diff --git a/src/calibre/utils/Zeroconf.py b/src/calibre/utils/Zeroconf.py deleted file mode 100644 index 62537988c5..0000000000 --- a/src/calibre/utils/Zeroconf.py +++ /dev/null @@ -1,1654 +0,0 @@ -""" Multicast DNS Service Discovery for Python - Copyright (C) 2003, Paul Scott-Murphy - Copyright (C) 2009, Alexander Solovyov - - This module provides a framework for the use of DNS Service Discovery - using IP multicast. It has been tested against the JRendezvous - implementation from StrangeBerry, - against the mDNSResponder from Mac OS X 10.3.8, 10.5.6, and against - Avahi library under various Linux distributions. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - -""" - - -"""0.13 update - fix IPv6 support - some cleanups in code""" - -"""0.12 update - allow selection of binding interface - typo fix - Thanks A. M. Kuchlingi - removed all use of word 'Rendezvous' - this is an API change""" - -"""0.11 update - correction to comments for addListener method - support for new record types seen from OS X - - IPv6 address - - hostinfo - ignore unknown DNS record types - fixes to name decoding - works alongside other processes using port 5353 (e.g. on Mac OS X) - tested against Mac OS X 10.3.2's mDNSResponder - corrections to removal of list entries for service browser""" - -"""0.10 update - Jonathon Paisley contributed these corrections: - always multicast replies, even when query is unicast - correct a pointer encoding problem - can now write records in any order - traceback shown on failure - better TXT record parsing - server is now separate from name - can cancel a service browser - - modified some unit tests to accommodate these changes""" - -"""0.09 update - remove all records on service unregistration - fix DOS security problem with readName""" - -"""0.08 update - changed licensing to LGPL""" - -"""0.07 update - faster shutdown on engine - pointer encoding of outgoing names - ServiceBrowser now works - new unit tests""" - -"""0.06 update - small improvements with unit tests - added defined exception types - new style objects - fixed hostname/interface problem - fixed socket timeout problem - fixed addServiceListener() typo bug - using select() for socket reads - tested on Debian unstable with Python 2.2.2""" - -"""0.05 update - ensure case insensitivty on domain names - support for unicast DNS queries""" - -"""0.04 update - added some unit tests - added __ne__ adjuncts where required - ensure names end in '.local.' - timeout on receiving socket for clean shutdown""" - -__author__ = "Paul Scott-Murphy" -__email__ = "paul at scott dash murphy dot com" -__version__ = "0.12" - -import string -import time -import struct -import socket -import threading -import select -import traceback -import numbers -from functools import reduce - -__all__ = ["Zeroconf", "ServiceInfo", "ServiceBrowser"] - -# hook for threads - -globals()['_GLOBAL_DONE'] = 0 - -# Some timing constants - -_UNREGISTER_TIME = 125 -_CHECK_TIME = 175 -_REGISTER_TIME = 225 -_LISTENER_TIME = 200 -_BROWSER_TIME = 500 - -# Some DNS constants - -_MDNS_ADDR = '224.0.0.251' -_MDNS_PORT = 5353 -_DNS_PORT = 53 -_DNS_TTL = 60 * 60 -# one hour default TTL - -_MAX_MSG_TYPICAL = 1460 # unused -_MAX_MSG_ABSOLUTE = 8972 - -_FLAGS_QR_MASK = 0x8000 # query response mask -_FLAGS_QR_QUERY = 0x0000 # query -_FLAGS_QR_RESPONSE = 0x8000 # response - -_FLAGS_AA = 0x0400 # Authorative answer -_FLAGS_TC = 0x0200 # Truncated -_FLAGS_RD = 0x0100 # Recursion desired -_FLAGS_RA = 0x8000 # Recursion available - -_FLAGS_Z = 0x0040 # Zero -_FLAGS_AD = 0x0020 # Authentic data -_FLAGS_CD = 0x0010 # Checking disabled - -_CLASS_IN = 1 -_CLASS_CS = 2 -_CLASS_CH = 3 -_CLASS_HS = 4 -_CLASS_NONE = 254 -_CLASS_ANY = 255 -_CLASS_MASK = 0x7FFF -_CLASS_UNIQUE = 0x8000 - -_TYPE_A = 1 -_TYPE_NS = 2 -_TYPE_MD = 3 -_TYPE_MF = 4 -_TYPE_CNAME = 5 -_TYPE_SOA = 6 -_TYPE_MB = 7 -_TYPE_MG = 8 -_TYPE_MR = 9 -_TYPE_NULL = 10 -_TYPE_WKS = 11 -_TYPE_PTR = 12 -_TYPE_HINFO = 13 -_TYPE_MINFO = 14 -_TYPE_MX = 15 -_TYPE_TXT = 16 -_TYPE_AAAA = 28 -_TYPE_SRV = 33 -_TYPE_ANY = 255 - -# Mapping constants to names - -_CLASSES = {_CLASS_IN : "in", - _CLASS_CS : "cs", - _CLASS_CH : "ch", - _CLASS_HS : "hs", - _CLASS_NONE : "none", - _CLASS_ANY : "any"} - -_TYPES = {_TYPE_A : "a", - _TYPE_NS : "ns", - _TYPE_MD : "md", - _TYPE_MF : "mf", - _TYPE_CNAME : "cname", - _TYPE_SOA : "soa", - _TYPE_MB : "mb", - _TYPE_MG : "mg", - _TYPE_MR : "mr", - _TYPE_NULL : "null", - _TYPE_WKS : "wks", - _TYPE_PTR : "ptr", - _TYPE_HINFO : "hinfo", - _TYPE_MINFO : "minfo", - _TYPE_MX : "mx", - _TYPE_TXT : "txt", - _TYPE_AAAA : "quada", - _TYPE_SRV : "srv", - _TYPE_ANY : "any"} - -# utility functions - - -def currentTimeMillis(): - """Current system time in milliseconds""" - return time.time() * 1000 - - -def ntop(address): - """Convert address to its string representation""" - af = len(address) == 4 and socket.AF_INET or socket.AF_INET6 - return socket.inet_ntop(af, address) - - -def address_type(address): - """Return appropriate record type for an address""" - return len(address) == 4 and _TYPE_A or _TYPE_AAAA - -# Exceptions - - -class MalformedPacketException(Exception): - pass - - -class NonLocalNameException(Exception): - pass - - -class NonUniqueNameException(Exception): - pass - - -class NamePartTooLongException(Exception): - pass - - -class AbstractMethodException(Exception): - pass - - -class BadTypeInNameException(Exception): - pass - - -class BadDomainName(Exception): - - def __init__(self, pos): - Exception.__init__(self, "at position " + str(pos)) - - -class BadDomainNameCircular(BadDomainName): - pass - -# implementation classes - - -class DNSEntry(object): - - """A DNS entry""" - - def __init__(self, name, type, clazz): - self.key = string.lower(name) - self.name = name - self.type = type - self.clazz = clazz & _CLASS_MASK - self.unique = (clazz & _CLASS_UNIQUE) != 0 - - def __eq__(self, other): - """Equality test on name, type, and class""" - if isinstance(other, DNSEntry): - return self.name == other.name and self.type == other.type and self.clazz == other.clazz - return 0 - - def __ne__(self, other): - """Non-equality test""" - return not self.__eq__(other) - - def getClazz(self, clazz): - """Class accessor""" - try: - return _CLASSES[clazz] - except: - return "?(%s)" % (clazz) - - def getType(self, type): - """Type accessor""" - try: - return _TYPES[type] - except: - return "?(%s)" % (type) - - def toString(self, hdr, other): - """String representation with additional information""" - result = "%s[%s,%s" % (hdr, self.getType(self.type), self.getClazz(self.clazz)) - if self.unique: - result += "-unique," - else: - result += "," - result += self.name - if other is not None: - result += ",%s]" % (other) - else: - result += "]" - return result - - -class DNSQuestion(DNSEntry): - - """A DNS question entry""" - - def __init__(self, name, type, clazz): - if not name.endswith(".local."): - raise NonLocalNameException(name) - DNSEntry.__init__(self, name, type, clazz) - - def answeredBy(self, rec): - """Returns true if the question is answered by the record""" - return self.clazz == rec.clazz and (self.type == rec.type or self.type == _TYPE_ANY) and self.name == rec.name - - def __repr__(self): - """String representation""" - return DNSEntry.toString(self, "question", None) - - -class DNSRecord(DNSEntry): - - """A DNS record - like a DNS entry, but has a TTL""" - - def __init__(self, name, type, clazz, ttl): - DNSEntry.__init__(self, name, type, clazz) - self.ttl = ttl - self.created = currentTimeMillis() - - def __eq__(self, other): - """Tests equality as per DNSRecord""" - if isinstance(other, DNSRecord): - return DNSEntry.__eq__(self, other) - return 0 - - def suppressedBy(self, msg): - """Returns true if any answer in a message can suffice for the - information held in this record.""" - for record in msg.answers: - if self.suppressedByAnswer(record): - return 1 - return 0 - - def suppressedByAnswer(self, other): - """Returns true if another record has same name, type and class, - and if its TTL is at least half of this record's.""" - if self == other and other.ttl > (self.ttl / 2): - return 1 - return 0 - - def getExpirationTime(self, percent): - """Returns the time at which this record will have expired - by a certain percentage.""" - return self.created + (percent * self.ttl * 10) - - def getRemainingTTL(self, now): - """Returns the remaining TTL in seconds.""" - return max(0, (self.getExpirationTime(100) - now) / 1000) - - def isExpired(self, now): - """Returns true if this record has expired.""" - return self.getExpirationTime(100) <= now - - def isStale(self, now): - """Returns true if this record is at least half way expired.""" - return self.getExpirationTime(50) <= now - - def resetTTL(self, other): - """Sets this record's TTL and created time to that of - another record.""" - self.created = other.created - self.ttl = other.ttl - - def write(self, out): - """Abstract method""" - raise AbstractMethodException - - def toString(self, other): - """String representation with addtional information""" - arg = "%s/%s,%s" % (self.ttl, self.getRemainingTTL(currentTimeMillis()), other) - return DNSEntry.toString(self, "record", arg) - - -class DNSAddress(DNSRecord): - - """A DNS address record""" - - def __init__(self, name, type, clazz, ttl, address): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.address = address - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeString(self.address, len(self.address)) - - def __eq__(self, other): - """Tests equality on address""" - if isinstance(other, DNSAddress): - return self.address == other.address - return 0 - - def __repr__(self): - """String representation""" - try: - return 'record[%s]' % ntop(self.address) - except: - return 'record[%s]' % self.address - - -class DNSHinfo(DNSRecord): - - """A DNS host information record""" - - def __init__(self, name, type, clazz, ttl, cpu, os): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.cpu = cpu - self.os = os - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeString(self.cpu, len(self.cpu)) - out.writeString(self.os, len(self.os)) - - def __eq__(self, other): - """Tests equality on cpu and os""" - if isinstance(other, DNSHinfo): - return self.cpu == other.cpu and self.os == other.os - return 0 - - def __repr__(self): - """String representation""" - return self.cpu + " " + self.os - - -class DNSPointer(DNSRecord): - - """A DNS pointer record""" - - def __init__(self, name, type, clazz, ttl, alias): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.alias = alias - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeName(self.alias) - - def __eq__(self, other): - """Tests equality on alias""" - if isinstance(other, DNSPointer): - return self.alias == other.alias - return 0 - - def __repr__(self): - """String representation""" - return self.toString(self.alias) - - -class DNSText(DNSRecord): - - """A DNS text record""" - - def __init__(self, name, type, clazz, ttl, text): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.text = text - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeString(self.text, len(self.text)) - - def __eq__(self, other): - """Tests equality on text""" - if isinstance(other, DNSText): - return self.text == other.text - return 0 - - def __repr__(self): - """String representation""" - if len(self.text) > 10: - return self.toString(self.text[:7] + "...") - else: - return self.toString(self.text) - - -class DNSService(DNSRecord): - - """A DNS service record""" - - def __init__(self, name, type, clazz, ttl, priority, weight, port, server): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.priority = priority - self.weight = weight - self.port = port - self.server = server - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeShort(self.priority) - out.writeShort(self.weight) - out.writeShort(self.port) - out.writeName(self.server) - - def __eq__(self, other): - """Tests equality on priority, weight, port and server""" - if isinstance(other, DNSService): - return self.priority == other.priority and self.weight == other.weight and self.port == other.port and self.server == other.server - return 0 - - def __repr__(self): - """String representation""" - return self.toString("%s:%s" % (self.server, self.port)) - - -class DNSIncoming(object): - - """Object representation of an incoming DNS packet""" - - def __init__(self, data): - """Constructor from string holding bytes of packet""" - self.offset = 0 - self.data = data - self.questions = [] - self.answers = [] - self.numQuestions = 0 - self.numAnswers = 0 - self.numAuthorities = 0 - self.numAdditionals = 0 - - self.readHeader() - self.readQuestions() - self.readOthers() - - def readHeader(self): - """Reads header portion of packet""" - format = '!HHHHHH' - length = struct.calcsize(format) - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - - self.id = info[0] - self.flags = info[1] - self.numQuestions = info[2] - self.numAnswers = info[3] - self.numAuthorities = info[4] - self.numAdditionals = info[5] - - def readQuestions(self): - """Reads questions section of packet""" - format = '!HH' - length = struct.calcsize(format) - for i in range(0, self.numQuestions): - name = self.readName() - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - - try: - question = DNSQuestion(name, info[0], info[1]) - self.questions.append(question) - except NonLocalNameException: - pass - - def readInt(self): - """Reads an integer from the packet""" - format = '!I' - length = struct.calcsize(format) - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - return info[0] - - def readCharacterString(self): - """Reads a character string from the packet""" - length = ord(self.data[self.offset]) - self.offset += 1 - return self.readString(length) - - def readString(self, len): - """Reads a string of a given length from the packet""" - format = '!' + str(len) + 's' - length = struct.calcsize(format) - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - return info[0] - - def readUnsignedShort(self): - """Reads an unsigned short from the packet""" - format = '!H' - length = struct.calcsize(format) - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - return info[0] - - def readOthers(self): - """Reads the answers, authorities and additionals section of the packet""" - format = '!HHiH' - length = struct.calcsize(format) - n = self.numAnswers + self.numAuthorities + self.numAdditionals - for i in range(0, n): - domain = self.readName() - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - - rec = None - if info[0] == _TYPE_A: - rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(4)) - elif info[0] == _TYPE_CNAME or info[0] == _TYPE_PTR: - rec = DNSPointer(domain, info[0], info[1], info[2], self.readName()) - elif info[0] == _TYPE_TXT: - rec = DNSText(domain, info[0], info[1], info[2], self.readString(info[3])) - elif info[0] == _TYPE_SRV: - rec = DNSService(domain, info[0], info[1], info[2], self.readUnsignedShort(), - self.readUnsignedShort(), self.readUnsignedShort(), self.readName()) - elif info[0] == _TYPE_HINFO: - rec = DNSHinfo(domain, info[0], info[1], info[2], self.readCharacterString(), self.readCharacterString()) - elif info[0] == _TYPE_AAAA: - rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(16)) - else: - # Skip unknown record type (using DNS length field) - self.offset += info[3] - - if rec is not None: - self.answers.append(rec) - - def isQuery(self): - """Returns true if this is a query""" - return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY - - def isResponse(self): - """Returns true if this is a response""" - return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_RESPONSE - - def readUTF(self, offset, len): - """Reads a UTF-8 string of a given length from the packet""" - return self.data[offset:offset+len].decode('utf-8') - - def readName(self): - """Reads a domain name from the packet""" - result = '' - off = self.offset - next = -1 - first = off - - while 1: - try: - len = ord(self.data[off]) - off += 1 - if len == 0: - break - t = len & 0xC0 - if t == 0x00: - result = ''.join((result, self.readUTF(off, len) + '.')) - off += len - elif t == 0xC0: - if next < 0: - next = off + 1 - off = ((len & 0x3F) << 8) | ord(self.data[off]) - if off >= first: - raise BadDomainNameCircular(off) - first = off - else: - raise BadDomainName(off) - except IndexError: - raise MalformedPacketException() - - if next >= 0: - self.offset = next - else: - self.offset = off - - return result - - -class DNSOutgoing(object): - - """Object representation of an outgoing packet""" - - def __init__(self, flags, multicast=1): - self.finished = 0 - self.id = 0 - self.multicast = multicast - self.flags = flags - self.names = {} - self.data = [] - self.size = 12 - - self.questions = [] - self.answers = [] - self.authorities = [] - self.additionals = [] - - def addQuestion(self, record): - """Adds a question""" - self.questions.append(record) - - def addAnswer(self, inp, record): - """Adds an answer""" - if not record.suppressedBy(inp): - self.addAnswerAtTime(record, 0) - - def addAnswerAtTime(self, record, now): - """Adds an answer if if does not expire by a certain time""" - if record is not None: - if now == 0 or not record.isExpired(now): - self.answers.append((record, now)) - - def addAuthorativeAnswer(self, record): - """Adds an authoritative answer""" - self.authorities.append(record) - - def addAdditionalAnswer(self, record): - """Adds an additional answer""" - self.additionals.append(record) - - def writeByte(self, value): - """Writes a single byte to the packet""" - format = '!c' - self.data.append(struct.pack(format, chr(value))) - self.size += 1 - - def insertShort(self, index, value): - """Inserts an unsigned short in a certain position in the packet""" - format = '!H' - self.data.insert(index, struct.pack(format, value)) - self.size += 2 - - def writeShort(self, value): - """Writes an unsigned short to the packet""" - format = '!H' - self.data.append(struct.pack(format, value)) - self.size += 2 - - def writeInt(self, value): - """Writes an unsigned integer to the packet""" - format = '!I' - self.data.append(struct.pack(format, int(value))) - self.size += 4 - - def writeString(self, value, length): - """Writes a string to the packet""" - format = '!' + str(length) + 's' - self.data.append(struct.pack(format, value)) - self.size += length - - def writeUTF(self, s): - """Writes a UTF-8 string of a given length to the packet""" - utfstr = s.encode('utf-8') - length = len(utfstr) - if length > 64: - raise NamePartTooLongException - self.writeByte(length) - self.writeString(utfstr, length) - - def writeName(self, name): - """Writes a domain name to the packet""" - - try: - # Find existing instance of this name in packet - # - index = self.names[name] - except KeyError: - # No record of this name already, so write it - # out as normal, recording the location of the name - # for future pointers to it. - # - self.names[name] = self.size - parts = name.split('.') - if parts[-1] == '': - parts = parts[:-1] - for part in parts: - self.writeUTF(part) - self.writeByte(0) - return - - # An index was found, so write a pointer to it - # - self.writeByte((index >> 8) | 0xC0) - self.writeByte(index) - - def writeQuestion(self, question): - """Writes a question to the packet""" - self.writeName(question.name) - self.writeShort(question.type) - self.writeShort(question.clazz) - - def writeRecord(self, record, now): - """Writes a record (answer, authoritative answer, additional) to - the packet""" - self.writeName(record.name) - self.writeShort(record.type) - if record.unique and self.multicast: - self.writeShort(record.clazz | _CLASS_UNIQUE) - else: - self.writeShort(record.clazz) - if now == 0: - self.writeInt(record.ttl) - else: - self.writeInt(record.getRemainingTTL(now)) - index = len(self.data) - # Adjust size for the short we will write before this record - # - self.size += 2 - record.write(self) - self.size -= 2 - - length = len(''.join(self.data[index:])) - self.insertShort(index, length) # Here is the short we adjusted for - - def packet(self): - """Returns a string containing the packet's bytes - - No further parts should be added to the packet once this - is done.""" - if not self.finished: - self.finished = 1 - for question in self.questions: - self.writeQuestion(question) - for answer, atime in self.answers: - self.writeRecord(answer, atime) - for authority in self.authorities: - self.writeRecord(authority, 0) - for additional in self.additionals: - self.writeRecord(additional, 0) - - self.insertShort(0, len(self.additionals)) - self.insertShort(0, len(self.authorities)) - self.insertShort(0, len(self.answers)) - self.insertShort(0, len(self.questions)) - self.insertShort(0, self.flags) - if self.multicast: - self.insertShort(0, 0) - else: - self.insertShort(0, self.id) - return ''.join(self.data) - - -class DNSCache(object): - - """A cache of DNS entries""" - - def __init__(self): - self.cache = {} - - def add(self, entry): - """Adds an entry""" - try: - list = self.cache[entry.key] - except: - list = self.cache[entry.key] = [] - list.append(entry) - - def remove(self, entry): - """Removes an entry""" - try: - list = self.cache[entry.key] - list.remove(entry) - except: - pass - - def get(self, entry): - """Gets an entry by key. Will return None if there is no - matching entry.""" - try: - list = self.cache[entry.key] - return list[list.index(entry)] - except: - return None - - def getByDetails(self, name, type, clazz): - """Gets an entry by details. Will return None if there is - no matching entry.""" - entry = DNSEntry(name, type, clazz) - return self.get(entry) - - def entriesWithName(self, name): - """Returns a list of entries whose key matches the name.""" - try: - return self.cache[name] - except: - return [] - - def entries(self): - """Returns a list of all entries""" - def add(x, y): - return x+y - try: - return reduce(add, self.cache.values()) - except: - return [] - - -class Engine(threading.Thread): - - """An engine wraps read access to sockets, allowing objects that - need to receive data from sockets to be called back when the - sockets are ready. - - A reader needs a handle_read() method, which is called when the socket - it is interested in is ready for reading. - - Writers are not implemented here, because we only send short - packets. - """ - - def __init__(self, zeroconf): - threading.Thread.__init__(self) - self.zeroconf = zeroconf - self.readers = {} # maps socket to reader - self.timeout = 5 - self.condition = threading.Condition() - self.setDaemon(True) # By Kovid - self.start() - - def run(self): - while not globals()['_GLOBAL_DONE']: - rs = self.getReaders() - if len(rs) == 0: - # No sockets to manage, but we wait for the timeout - # or addition of a socket - # - self.condition.acquire() - self.condition.wait(self.timeout) - self.condition.release() - else: - from calibre.constants import DEBUG - try: - rr, wr, er = select.select(rs, [], [], self.timeout) - if globals()['_GLOBAL_DONE']: - continue - for sock in rr: - try: - self.readers[sock].handle_read() - except MalformedPacketException: - pass - except: - if DEBUG: - traceback.print_exc() - except: - pass - - def getReaders(self): - self.condition.acquire() - result = self.readers.keys() - self.condition.release() - return result - - def addReader(self, reader, socket): - self.condition.acquire() - self.readers[socket] = reader - self.condition.notify() - self.condition.release() - - def delReader(self, socket): - self.condition.acquire() - del(self.readers[socket]) - self.condition.notify() - self.condition.release() - - def notify(self): - self.condition.acquire() - self.condition.notify() - self.condition.release() - - -class Listener(object): - - """A Listener is used by this module to listen on the multicast - group to which DNS messages are sent, allowing the implementation - to cache information as it arrives. - - It requires registration with an Engine object in order to have - the read() method called when a socket is availble for reading.""" - - def __init__(self, zeroconf): - self.zeroconf = zeroconf - self.zeroconf.engine.addReader(self, self.zeroconf.socket) - - def handle_read(self): - data, (addr, port) = self.zeroconf.socket.recvfrom(_MAX_MSG_ABSOLUTE) - self.data = data - msg = DNSIncoming(data) - if msg.isQuery(): - # Always multicast responses - # - if port == _MDNS_PORT: - self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) - # If it's not a multicast query, reply via unicast - # and multicast - # - elif port == _DNS_PORT: - self.zeroconf.handleQuery(msg, addr, port) - self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) - else: - self.zeroconf.handleResponse(msg) - - -class Reaper(threading.Thread): - - """A Reaper is used by this module to remove cache entries that - have expired.""" - - def __init__(self, zeroconf): - threading.Thread.__init__(self) - self.setDaemon(True) # By Kovid - self.zeroconf = zeroconf - self.start() - - def run(self): - while 1: - try: - self.zeroconf.wait(10 * 1000) - except TypeError: # By Kovid - globals()['_GLOBAL_DONE'] = 1 - return - if globals()['_GLOBAL_DONE']: - return - try: - # can get here in a race condition with shutdown. Swallow the - # exception and run around the loop again. - now = currentTimeMillis() - for record in self.zeroconf.cache.entries(): - if record.isExpired(now): - self.zeroconf.updateRecord(now, record) - self.zeroconf.cache.remove(record) - except: - pass - - -class ServiceBrowser(threading.Thread): - - """Used to browse for a service of a specific type. - - The listener object will have its addService() and - removeService() methods called when this browser - discovers changes in the services availability.""" - - def __init__(self, zeroconf, type, listener): - """Creates a browser for a specific type""" - threading.Thread.__init__(self) - self.zeroconf = zeroconf - self.type = type - self.listener = listener - self.services = {} - self.nextTime = currentTimeMillis() - self.delay = _BROWSER_TIME - self.list = [] - - self.done = 0 - - self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) - self.start() - - def updateRecord(self, zeroconf, now, record): - """Callback invoked by Zeroconf when new information arrives. - - Updates information required by browser in the Zeroconf cache.""" - if record.type == _TYPE_PTR and record.name == self.type: - expired = record.isExpired(now) - try: - oldrecord = self.services[record.alias.lower()] - if not expired: - oldrecord.resetTTL(record) - else: - del(self.services[record.alias.lower()]) - callback = lambda x: self.listener.removeService(x, self.type, record.alias) - self.list.append(callback) - return - except: - if not expired: - self.services[record.alias.lower()] = record - callback = lambda x: self.listener.addService(x, self.type, record.alias) - self.list.append(callback) - - expires = record.getExpirationTime(75) - if expires < self.nextTime: - self.nextTime = expires - - def cancel(self): - self.done = 1 - self.zeroconf.notifyAll() - - def run(self): - while 1: - event = None - now = currentTimeMillis() - if len(self.list) == 0 and self.nextTime > now: - self.zeroconf.wait(self.nextTime - now) - if globals()['_GLOBAL_DONE'] or self.done: - return - now = currentTimeMillis() - - if self.nextTime <= now: - out = DNSOutgoing(_FLAGS_QR_QUERY) - out.addQuestion(DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) - for record in self.services.values(): - if not record.isExpired(now): - out.addAnswerAtTime(record, now) - self.zeroconf.send(out) - self.nextTime = now + self.delay - self.delay = min(20 * 1000, self.delay * 2) - - if len(self.list) > 0: - event = self.list.pop(0) - - if event is not None: - event(self.zeroconf) - - -class ServiceInfo(object): - - """Service information""" - - def __init__(self, type, name, address=None, port=None, weight=0, priority=0, properties=None, server=None): - """Create a service description. - - type: fully qualified service type name - name: fully qualified service name - address: IP address as unsigned short, network byte order - port: port that the service runs on - weight: weight of the service - priority: priority of the service - properties: dictionary of properties (or a string holding the bytes for the text field) - server: fully qualified name for service host (defaults to name)""" - - if not name.endswith(type): - raise BadTypeInNameException - self.type = type - self.name = name - self.address = address - if address: - self.ip_type = address_type(address) - self.port = port - self.weight = weight - self.priority = priority - if server: - self.server = server - else: - self.server = name - self.setProperties(properties) - - def setProperties(self, properties): - """Sets properties and text of this info from a dictionary""" - if isinstance(properties, dict): - self.properties = properties - list = [] - result = '' - for key in properties: - value = properties[key] - if value is None: - suffix = '' - elif isinstance(value, bytes): - suffix = value - elif isinstance(value, numbers.Integral): - suffix = value and 'true' or 'false' - else: - suffix = '' - list.append('='.join((key, suffix))) - for item in list: - result = ''.join((result, struct.pack('!c', chr(len(item))), item)) - self.text = result - else: - self.text = properties - - def setText(self, text): - """Sets properties and text given a text field""" - self.text = text - try: - result = {} - end = len(text) - index = 0 - strs = [] - while index < end: - length = ord(text[index]) - index += 1 - strs.append(text[index:index+length]) - index += length - - for s in strs: - eindex = s.find('=') - if eindex == -1: - # No equals sign at all - key = s - value = 0 - else: - key = s[:eindex] - value = s[eindex+1:] - if value == 'true': - value = 1 - elif value == 'false' or not value: - value = 0 - - # Only update non-existent properties - if key and result.get(key) == None: # noqa - result[key] = value - - self.properties = result - except: - traceback.print_exc() - self.properties = None - - def getType(self): - """Type accessor""" - return self.type - - def getName(self): - """Name accessor""" - if self.type is not None and self.name.endswith("." + self.type): - return self.name[:len(self.name) - len(self.type) - 1] - return self.name - - def getAddress(self): - """Address accessor""" - return self.address - - def getPort(self): - """Port accessor""" - return self.port - - def getPriority(self): - """Pirority accessor""" - return self.priority - - def getWeight(self): - """Weight accessor""" - return self.weight - - def getProperties(self): - """Properties accessor""" - return self.properties - - def getText(self): - """Text accessor""" - return self.text - - def getServer(self): - """Server accessor""" - return self.server - - def updateRecord(self, zeroconf, now, record): - """Updates service information from a DNS record""" - if record is None or record.isExpired(now): - return - if (record.type in (_TYPE_A, _TYPE_AAAA) and - record.name == self.server): - self.address = record.address - elif record.type == _TYPE_SRV and record.name == self.name: - self.server = record.server - self.port = record.port - self.weight = record.weight - self.priority = record.priority - self.updateRecord(zeroconf, now, zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN)) - elif record.type == _TYPE_TXT and record.name == self.name: - self.setText(record.text) - - def request(self, zeroconf, timeout): - """Returns true if the service could be discovered on the - network, and updates this object with details discovered. - """ - now = currentTimeMillis() - delay = _LISTENER_TIME - next = now + delay - last = now + timeout - result = 0 - try: - zeroconf.addListener(self, DNSQuestion(self.name, _TYPE_ANY, _CLASS_IN)) - while self.server is None or self.address is None or self.text is None: - if last <= now: - return 0 - if next <= now: - out = DNSOutgoing(_FLAGS_QR_QUERY) - out.addQuestion(DNSQuestion(self.name, _TYPE_SRV, _CLASS_IN)) - out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_SRV, _CLASS_IN), now) - out.addQuestion(DNSQuestion(self.name, _TYPE_TXT, _CLASS_IN)) - out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_TXT, _CLASS_IN), now) - if self.server is not None: - out.addQuestion(DNSQuestion(self.server, _TYPE_A, _CLASS_IN)) - out.addAnswerAtTime(zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN), now) - zeroconf.send(out) - next = now + delay - delay = delay * 2 - - zeroconf.wait(min(next, last) - now) - now = currentTimeMillis() - result = 1 - finally: - zeroconf.removeListener(self) - - return result - - def __eq__(self, other): - """Tests equality of service name""" - if isinstance(other, ServiceInfo): - return other.name == self.name - return 0 - - def __ne__(self, other): - """Non-equality test""" - return not self.__eq__(other) - - def __repr__(self): - """String representation""" - result = "service[%s,%s:%s," % (self.name, ntop(self.getAddress()), self.port) - if self.text is None: - result += "None" - else: - if len(self.text) < 20: - result += self.text - else: - result += self.text[:17] + "..." - result += "]" - return result - - -class Zeroconf(object): - - """Implementation of Zeroconf Multicast DNS Service Discovery - - Supports registration, unregistration, queries and browsing. - """ - - def __init__(self, bindaddress=None): - """Creates an instance of the Zeroconf class, establishing - multicast communications, listening and reaping threads.""" - globals()['_GLOBAL_DONE'] = 0 - if bindaddress is None: - self.intf = socket.gethostbyname(socket.gethostname()) - else: - self.intf = bindaddress - self.group = ('', _MDNS_PORT) - self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - try: - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - except: - # SO_REUSEADDR should be equivalent to SO_REUSEPORT for - # multicast UDP sockets (p 731, "TCP/IP Illustrated, - # Volume 2"), but some BSD-derived systems require - # SO_REUSEPORT to be specified explicity. Also, not all - # versions of Python have SO_REUSEPORT available. So - # if you're on a BSD-based system, and haven't upgraded - # to Python 2.3 yet, you may find this library doesn't - # work as expected. - # - pass - self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) - self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) - try: - self.socket.bind(self.group) - except: - # Some versions of linux raise an exception even though - # the SO_REUSE* options have been set, so ignore it - # - pass - # self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.intf) + socket.inet_aton('0.0.0.0')) - self.socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) - - self.listeners = [] - self.browsers = [] - self.services = {} - self.servicetypes = {} - - self.cache = DNSCache() - - self.condition = threading.Condition() - - self.engine = Engine(self) - self.listener = Listener(self) - self.reaper = Reaper(self) - - def isLoopback(self): - return self.intf.startswith("127.0.0.1") - - def isLinklocal(self): - return self.intf.startswith("169.254.") - - def wait(self, timeout): - """Calling thread waits for a given number of milliseconds or - until notified.""" - self.condition.acquire() - self.condition.wait(timeout/1000) - self.condition.release() - - def notifyAll(self): - """Notifies all waiting threads""" - self.condition.acquire() - self.condition.notifyAll() - self.condition.release() - - def getServiceInfo(self, type, name, timeout=3000): - """Returns network's service information for a particular - name and type, or None if no service matches by the timeout, - which defaults to 3 seconds.""" - info = ServiceInfo(type, name) - if info.request(self, timeout): - return info - return None - get_service_info = getServiceInfo - - def addServiceListener(self, type, listener): - """Adds a listener for a particular service type. This object - will then have its updateRecord method called when information - arrives for that type.""" - self.removeServiceListener(listener) - self.browsers.append(ServiceBrowser(self, type, listener)) - - def removeServiceListener(self, listener): - """Removes a listener from the set that is currently listening.""" - for browser in self.browsers: - if browser.listener == listener: - browser.cancel() - del(browser) - - def registerService(self, info, ttl=_DNS_TTL): - """Registers service information to the network with a default TTL - of 60 seconds. Zeroconf will then respond to requests for - information for that service. The name of the service may be - changed if needed to make it unique on the network.""" - self.checkService(info) - self.services[info.name.lower()] = info - self.servicetypes[info.type] = self.servicetypes.get(info.type, 0) + 1 - now = currentTimeMillis() - nextTime = now - i = 0 - while i < 3: - if now < nextTime: - self.wait(nextTime - now) - now = currentTimeMillis() - continue - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, ttl, info.name), 0) - out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, ttl, info.priority, info.weight, info.port, info.server), 0) - out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, ttl, info.text), 0) - if info.address: - out.addAnswerAtTime(DNSAddress(info.server, info.ip_type, _CLASS_IN, ttl, info.address), 0) - self.send(out) - i += 1 - nextTime += _REGISTER_TIME - register_service = registerService - - def unregisterService(self, info): - """Unregister a service.""" - try: - del(self.services[info.name.lower()]) - if self.servicetypes[info.type]>1: - self.servicetypes[info.type]-=1 - else: - del self.servicetypes[info.type] - except: - pass - now = currentTimeMillis() - nextTime = now - i = 0 - while i < 3: - if now < nextTime: - self.wait(nextTime - now) - now = currentTimeMillis() - continue - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) - out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.name), 0) - out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) - if info.address: - out.addAnswerAtTime(DNSAddress(info.server, info.ip_type, _CLASS_IN, 0, info.address), 0) - self.send(out) - i += 1 - nextTime += _UNREGISTER_TIME - unregister_service = unregisterService - - def unregisterAllServices(self): - """Unregister all registered services.""" - if not self.services: - return - now = currentTimeMillis() - nextTime = now - i = 0 - while i < 3: - if now < nextTime: - self.wait(nextTime - now) - now = currentTimeMillis() - continue - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - for info in self.services.values(): - out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) - out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.server), 0) - out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) - if info.address: - out.addAnswerAtTime(DNSAddress(info.server, info.ip_type, _CLASS_IN, 0, info.address), 0) - self.send(out) - i += 1 - nextTime += _UNREGISTER_TIME - - def countRegisteredServices(self): - return len(self.services) - - def checkService(self, info): - """Checks the network for a unique service name, modifying the - ServiceInfo passed in if it is not unique.""" - now = currentTimeMillis() - nextTime = now - i = 0 - while i < 3: - for record in self.cache.entriesWithName(info.type): - if record.type == _TYPE_PTR and not record.isExpired(now) and record.alias == info.name: - if (info.name.find('.') < 0): - info.name = info.name + ".[" + info.address + ":" + info.port + "]." + info.type - self.checkService(info) - return - raise NonUniqueNameException - if now < nextTime: - self.wait(nextTime - now) - now = currentTimeMillis() - continue - out = DNSOutgoing(_FLAGS_QR_QUERY | _FLAGS_AA) - self.debug = out - out.addQuestion(DNSQuestion(info.type, _TYPE_PTR, _CLASS_IN)) - out.addAuthorativeAnswer(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, info.name)) - self.send(out) - i += 1 - nextTime += _CHECK_TIME - - def addListener(self, listener, question): - """Adds a listener for a given question. The listener will have - its updateRecord method called when information is available to - answer the question.""" - now = currentTimeMillis() - self.listeners.append(listener) - if question is not None: - for record in self.cache.entriesWithName(question.name): - if question.answeredBy(record) and not record.isExpired(now): - listener.updateRecord(self, now, record) - self.notifyAll() - - def removeListener(self, listener): - """Removes a listener.""" - try: - self.listeners.remove(listener) - self.notifyAll() - except: - pass - - def updateRecord(self, now, rec): - """Used to notify listeners of new information that has updated - a record.""" - for listener in self.listeners: - listener.updateRecord(self, now, rec) - self.notifyAll() - - def handleResponse(self, msg): - """Deal with incoming response packets. All answers - are held in the cache, and listeners are notified.""" - now = currentTimeMillis() - for record in msg.answers: - expired = record.isExpired(now) - if record in self.cache.entries(): - if expired: - self.cache.remove(record) - else: - entry = self.cache.get(record) - if entry is not None: - entry.resetTTL(record) - record = entry - else: - self.cache.add(record) - - self.updateRecord(now, record) - - def handleQuery(self, msg, addr, port): - """Deal with incoming query packets. Provides a response if - possible.""" - out = None - - # Support unicast client responses - # - if port != _MDNS_PORT: - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0) - for question in msg.questions: - out.addQuestion(question) - - for question in msg.questions: - if question.type == _TYPE_PTR: - if question.name == "_services._dns-sd._udp.local.": - for stype in self.servicetypes.keys(): - if out is None: - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - out.addAnswer(msg, DNSPointer("_services._dns-sd._udp.local.", _TYPE_PTR, _CLASS_IN, _DNS_TTL, stype)) - for service in self.services.values(): - if question.name == service.type: - if out is None: - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - out.addAnswer(msg, DNSPointer(service.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, service.name)) - else: - try: - if out is None: - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - - # Answer A record queries for any service addresses we know - if question.type in (_TYPE_A, _TYPE_AAAA, _TYPE_ANY): - for service in self.services.values(): - if service.server == question.name.lower(): - out.addAnswer( - msg, DNSAddress(question.name, address_type(service.address), _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) - - service = self.services.get(question.name.lower(), None) - if not service: - continue - - if question.type == _TYPE_SRV or question.type == _TYPE_ANY: - out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, - _DNS_TTL, service.priority, service.weight, service.port, service.server)) - if question.type == _TYPE_TXT or question.type == _TYPE_ANY: - out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text)) - if question.type == _TYPE_SRV: - out.addAdditionalAnswer(DNSAddress(service.server, address_type(service.address), _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) - except: - traceback.print_exc() - - if out is not None and out.answers: - out.id = msg.id - self.send(out, addr, port) - - def send(self, out, addr=_MDNS_ADDR, port=_MDNS_PORT): - """Sends an outgoing packet.""" - # This is a quick test to see if we can parse the packets we generate - # temp = DNSIncoming(out.packet()) - try: - self.socket.sendto(out.packet(), 0, (addr, port)) - except: - # Ignore this, it may be a temporary loss of network connection - pass - - def close(self): - """Ends the background threads, and prevent this instance from - servicing further queries.""" - if globals()['_GLOBAL_DONE'] == 0: - globals()['_GLOBAL_DONE'] = 1 - self.notifyAll() - self.engine.notify() - self.unregisterAllServices() - self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) - self.socket.close() - -# Test a few module features, including service registration, service -# query (for Zoe), and service unregistration. - - -if __name__ == '__main__': - print("Multicast DNS Service Discovery for Python, version", __version__) - r = Zeroconf() - print("1. Testing registration of a service...") - desc = {'version':'0.10','a':'test value', 'b':'another value'} - info = ServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc) - print(" Registering service...") - r.registerService(info) - print(" Registration done.") - print("2. Testing query of service information...") - print(" Getting ZOE service:", str(r.getServiceInfo("_http._tcp.local.", "ZOE._http._tcp.local."))) - print(" Query done.") - print("3. Testing query of own service...") - print(" Getting self:", str(r.getServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local."))) - print(" Query done.") - print("4. Testing unregister of service information...") - r.unregisterService(info) - print(" Unregister done.") - r.close() diff --git a/src/calibre/utils/complete.py b/src/calibre/utils/complete.py index 9bfe5e9b19..26e411ddb7 100644 --- a/src/calibre/utils/complete.py +++ b/src/calibre/utils/complete.py @@ -14,45 +14,8 @@ completion. import sys, os, shlex, glob, re -from polyglot.builtins import is_py3, unicode_type - -if is_py3: - prints = print -else: - def prints(*args, **kwargs): - ''' - Print unicode arguments safely by encoding them to preferred_encoding - Has the same signature as the print function from Python 3, except for the - additional keyword argument safe_encode, which if set to True will cause the - function to use repr when encoding fails. - ''' - file = kwargs.get('file', sys.stdout) - sep = kwargs.get('sep', ' ') - end = kwargs.get('end', '\n') - enc = 'utf-8' - safe_encode = kwargs.get('safe_encode', False) - for i, arg in enumerate(args): - if isinstance(arg, unicode_type): - try: - arg = arg.encode(enc) - except UnicodeEncodeError: - if not safe_encode: - raise - arg = repr(arg) - if not isinstance(arg, bytes): - arg = unicode_type(arg) - try: - arg = arg.encode(enc) - except UnicodeEncodeError: - if not safe_encode: - raise - arg = repr(arg) - - file.write(arg) - if i != len(args)-1: - file.write(sep) - file.write(end) +prints = print def split(src): diff --git a/src/calibre/utils/config_base.py b/src/calibre/utils/config_base.py index fffd1354c2..e2a35cb4cb 100644 --- a/src/calibre/utils/config_base.py +++ b/src/calibre/utils/config_base.py @@ -11,17 +11,14 @@ from collections import defaultdict from copy import deepcopy from calibre.utils.lock import ExclusiveFile -from calibre.constants import config_dir, CONFIG_DIR_MODE, ispy3, preferred_encoding, filesystem_encoding, iswindows +from calibre.constants import config_dir, CONFIG_DIR_MODE, preferred_encoding, filesystem_encoding, iswindows from polyglot.builtins import unicode_type, iteritems, map plugin_dir = os.path.join(config_dir, 'plugins') def parse_old_style(src): - if ispy3: - import pickle as cPickle - else: - import cPickle + import pickle as cPickle options = {'cPickle':cPickle} try: if not isinstance(src, unicode_type): diff --git a/src/calibre/utils/dbus_service.py b/src/calibre/utils/dbus_service.py index feac589e1a..e04b6524b6 100644 --- a/src/calibre/utils/dbus_service.py +++ b/src/calibre/utils/dbus_service.py @@ -45,7 +45,7 @@ from dbus.exceptions import ( from dbus.lowlevel import ErrorMessage, MethodReturnMessage, MethodCallMessage from dbus.proxies import LOCAL_PATH -from polyglot.builtins import itervalues, zip, is_py3, native_string_type +from polyglot.builtins import itervalues, zip, native_string_type class dbus_property(object): @@ -161,9 +161,6 @@ class _VariantSignature(object): """Return 'v' whenever called.""" return 'v' - if not is_py3: - next = __next__ - class BusName(object): """A base class for exporting your own Named Services across the Bus. diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index 1b5ca70c63..2687bb2862 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -12,7 +12,7 @@ from math import ceil from calibre import force_unicode, isbytestring, prints, sanitize_file_name from calibre.constants import ( - filesystem_encoding, iswindows, plugins, preferred_encoding, isosx, ispy3 + filesystem_encoding, iswindows, plugins, preferred_encoding, isosx ) from calibre.utils.localization import get_udc from polyglot.builtins import iteritems, itervalues, unicode_type, range @@ -629,14 +629,4 @@ def copytree_using_links(path, dest, dest_is_parent=True, filecopyfunc=copyfile) filecopyfunc(src, df) -if not ispy3 and not iswindows: - # On POSIX in python2 if you pass a unicode path to rmtree - # it tries to decode all filenames it encounters while walking - # the tree which leads to unicode errors on Linux where there - # can be non-decodeable filenames. - def rmtree(x, **kw): - if not isinstance(x, bytes): - x = x.encode('utf-8') - return shutil.rmtree(x, **kw) -else: - rmtree = shutil.rmtree +rmtree = shutil.rmtree diff --git a/src/calibre/utils/imghdr.py b/src/calibre/utils/imghdr.py index 72221e4939..f7bc9b0dc1 100644 --- a/src/calibre/utils/imghdr.py +++ b/src/calibre/utils/imghdr.py @@ -6,7 +6,6 @@ from struct import unpack, error import os from calibre.utils.speedups import ReadOnlyFileBuffer -from calibre.constants import ispy3 from polyglot.builtins import string_or_bytes, unicode_type """ Recognize image file formats and sizes based on their first few bytes.""" @@ -125,12 +124,8 @@ def jpeg_dimensions(stream): raise ValueError('Truncated JPEG data') return ans - if ispy3: - def read_byte(): - return read(1)[0] - else: - def read_byte(): - return ord(read(1)[0]) + def read_byte(): + return read(1)[0] x = None while True: diff --git a/src/calibre/utils/ipc/__init__.py b/src/calibre/utils/ipc/__init__.py index a2355fb7e3..b683a69a39 100644 --- a/src/calibre/utils/ipc/__init__.py +++ b/src/calibre/utils/ipc/__init__.py @@ -10,7 +10,7 @@ import os, errno, sys from threading import Thread from calibre import force_unicode -from calibre.constants import iswindows, get_windows_username, islinux, filesystem_encoding, ispy3 +from calibre.constants import iswindows, get_windows_username, islinux, filesystem_encoding from calibre.utils.filenames import ascii_filename from polyglot.functools import lru_cache @@ -48,8 +48,6 @@ def socket_address(which): from tempfile import gettempdir tmp = force_unicode(gettempdir(), filesystem_encoding) ans = os.path.join(tmp, sock_name) - if not ispy3 and not isinstance(ans, bytes): - ans = ans.encode(filesystem_encoding) return ans diff --git a/src/calibre/utils/ipc/launch.py b/src/calibre/utils/ipc/launch.py index 88b112c4b0..b31494a1b6 100644 --- a/src/calibre/utils/ipc/launch.py +++ b/src/calibre/utils/ipc/launch.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai - __license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' @@ -9,11 +8,11 @@ __docformat__ = 'restructuredtext en' import subprocess, os, sys, time from functools import partial -from calibre.constants import iswindows, isosx, isfrozen, filesystem_encoding, ispy3 +from calibre.constants import iswindows, isosx, isfrozen from calibre.utils.config import prefs from calibre.ptempfile import PersistentTemporaryFile, base_dir from calibre.utils.serialize import msgpack_dumps -from polyglot.builtins import iteritems, unicode_type, string_or_bytes, environ_item, native_string_type, getcwd +from polyglot.builtins import string_or_bytes, environ_item, native_string_type, getcwd from polyglot.binary import as_hex_unicode if iswindows: @@ -86,26 +85,7 @@ class Worker(object): @property def env(self): - if ispy3: - env = os.environ.copy() - else: - # We use this inefficient method of copying the environment variables - # because of non ascii env vars on windows. See https://bugs.launchpad.net/bugs/811191 - env = {} - for key in os.environ: - try: - val = os.environ[key] - if isinstance(val, unicode_type): - # On windows subprocess cannot handle unicode env vars - try: - val = val.encode(filesystem_encoding) - except ValueError: - val = val.encode('utf-8') - if isinstance(key, unicode_type): - key = key.encode('ascii') - env[key] = val - except: - pass + env = os.environ.copy() env[native_string_type('CALIBRE_WORKER')] = environ_item('1') td = as_hex_unicode(msgpack_dumps(base_dir())) env[native_string_type('CALIBRE_WORKER_TEMP_DIR')] = environ_item(td) @@ -156,22 +136,7 @@ class Worker(object): self._env = {} self.gui = gui self.job_name = job_name - if ispy3: - self._env = env.copy() - else: - # Windows cannot handle unicode env vars - for k, v in iteritems(env): - try: - if isinstance(k, unicode_type): - k = k.encode('ascii') - if isinstance(v, unicode_type): - try: - v = v.encode(filesystem_encoding) - except: - v = v.encode('utf-8') - self._env[k] = v - except: - pass + self._env = env.copy() def __call__(self, redirect_output=True, cwd=None, priority=None): ''' diff --git a/src/calibre/utils/ipc/server.py b/src/calibre/utils/ipc/server.py index 97a9ba84aa..cc34cf32ef 100644 --- a/src/calibre/utils/ipc/server.py +++ b/src/calibre/utils/ipc/server.py @@ -19,7 +19,7 @@ from multiprocessing.connection import Listener, arbitrary_address from threading import RLock, Thread from calibre import detect_ncpus as cpu_count, force_unicode -from calibre.constants import DEBUG, islinux, iswindows, ispy3 +from calibre.constants import DEBUG, islinux, iswindows from calibre.ptempfile import base_dir from calibre.utils.ipc import eintr_retry_call from calibre.utils.ipc.launch import Worker @@ -138,8 +138,6 @@ if islinux: prefix = '\0calibre-ipc-listener-%d-%%d' % os.getpid() while True: address = (prefix % next(_name_counter)) - if not ispy3 and not isinstance(address, bytes): - address = address.encode('ascii') try: l = LinuxListener(address=address, authkey=authkey, backlog=backlog) return address, l @@ -162,8 +160,6 @@ else: while max_tries > 0: max_tries -= 1 address = prefix % next(_name_counter) - if not ispy3 and not isinstance(address, bytes): - address = address.encode('utf-8') # multiprocessing needs bytes in python 2 try: return address, Listener(address=address, authkey=authkey, backlog=backlog) except EnvironmentError as err: diff --git a/src/calibre/utils/localization.py b/src/calibre/utils/localization.py index 11189f099f..50303c464c 100644 --- a/src/calibre/utils/localization.py +++ b/src/calibre/utils/localization.py @@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en' import os, locale, re, io, sys from gettext import GNUTranslations, NullTranslations -from polyglot.builtins import is_py3, iteritems, unicode_type +from polyglot.builtins import iteritems, unicode_type _available_translations = None @@ -268,10 +268,7 @@ def set_translators(): set_translators.lang = t.info().get('language') except Exception: pass - if is_py3: - t.install(names=('ngettext',)) - else: - t.install(unicode=True, names=('ngettext',)) + t.install(names=('ngettext',)) # Now that we have installed a translator, we have to retranslate the help # for the global prefs object as it was instantiated in get_lang(), before # the translator was installed. diff --git a/src/calibre/utils/lock.py b/src/calibre/utils/lock.py index b3bdf064f6..f17ce9bab7 100644 --- a/src/calibre/utils/lock.py +++ b/src/calibre/utils/lock.py @@ -11,7 +11,7 @@ import time from functools import partial from calibre.constants import ( - __appname__, fcntl, filesystem_encoding, islinux, isosx, iswindows, plugins, ispy3 + __appname__, fcntl, filesystem_encoding, islinux, isosx, iswindows, plugins ) from calibre.utils.monotonic import monotonic @@ -151,8 +151,6 @@ elif islinux: ) name = name address = '\0' + name.replace(' ', '_') - if not ispy3: - address = address.encode('utf-8') sock = socket.socket(family=socket.AF_UNIX) try: eintr_retry_call(sock.bind, address) diff --git a/src/calibre/utils/mdns.py b/src/calibre/utils/mdns.py index a25e3f2d8b..c82b1abe68 100644 --- a/src/calibre/utils/mdns.py +++ b/src/calibre/utils/mdns.py @@ -1,4 +1,3 @@ - __license__ = 'GPL 3' __copyright__ = '2009, Kovid Goyal ' __docformat__ = 'restructuredtext en' @@ -9,8 +8,6 @@ from threading import Thread from calibre.utils.filenames import ascii_text from calibre import force_unicode -from calibre.constants import ispy3 -from polyglot.builtins import iteritems, unicode_type _server = None @@ -110,10 +107,7 @@ def get_external_ip(): def start_server(): global _server if _server is None: - if ispy3: - from zeroconf import Zeroconf - else: - from calibre.utils.Zeroconf import Zeroconf + from zeroconf import Zeroconf try: _server = Zeroconf() except Exception: @@ -150,21 +144,7 @@ def create_service(desc, service_type, port, properties, add_hostname, use_ip_ad service_type = service_type+'.local.' service_name = desc + '.' + service_type server_name = hostname+'.local.' - if ispy3: - from zeroconf import ServiceInfo - else: - from calibre.utils.Zeroconf import ServiceInfo - - def enc(x): - if isinstance(x, unicode_type): - x = x.encode('ascii') - return x - - service_type = enc(service_type) - service_name = enc(service_name) - server_name = enc(server_name) - if properties: - properties = {enc(k): enc(v) for k, v in iteritems(properties)} + from zeroconf import ServiceInfo return ServiceInfo( service_type, service_name, diff --git a/src/calibre/utils/monotonic.c b/src/calibre/utils/monotonic.c deleted file mode 100644 index aecb2476fa..0000000000 --- a/src/calibre/utils/monotonic.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * monotonic.c - * Copyright (C) 2015 Kovid Goyal - * - * Distributed under terms of the GPL3 license. - */ - -#define UNICODE -#include - -/* To millisecond (10^-3) */ -#define SEC_TO_MS 1000 - -/* To microseconds (10^-6) */ -#define MS_TO_US 1000 -#define SEC_TO_US (SEC_TO_MS * MS_TO_US) - -/* To nanoseconds (10^-9) */ -#define US_TO_NS 1000 -#define MS_TO_NS (MS_TO_US * US_TO_NS) -#define SEC_TO_NS (SEC_TO_MS * MS_TO_NS) - -/* Conversion from nanoseconds */ -#define NS_TO_MS (1000 * 1000) -#define NS_TO_US (1000) - -#ifdef _MSC_VER -#include - -static PyObject* monotonic(PyObject *self, PyObject *args) { - return PyFloat_FromDouble(((double)GetTickCount64())/SEC_TO_MS); -} - -/* QueryPerformanceCounter() is wildly inaccurate, so we use the more stable - * the lower resolution GetTickCount64() (this is what python 3.x uses) - * static LARGE_INTEGER frequency = {0}, ts = {0}; - * static PyObject* monotonic(PyObject *self, PyObject *args) { - * if (!QueryPerformanceCounter(&ts)) { PyErr_SetFromWindowsErr(0); return NULL; } - * return PyFloat_FromDouble(((double)ts.QuadPart)/frequency.QuadPart); - * } - */ - -#elif defined(__APPLE__) -#include -static mach_timebase_info_data_t timebase = {0}; -static PyObject* monotonic(PyObject *self, PyObject *args) { - return PyFloat_FromDouble(((double)(mach_absolute_time() * timebase.numer) / timebase.denom)/SEC_TO_NS); -} - -#else -#include -static struct timespec ts = {0}; -#ifdef CLOCK_HIGHRES -const static clockid_t clk_id = CLOCK_HIGHRES; -#elif defined(CLOCK_MONOTONIC_RAW) -const static clockid_t clk_id = CLOCK_MONOTONIC_RAW; -#else -const static clockid_t clk_id = CLOCK_MONOTONIC; -#endif -static PyObject* monotonic(PyObject *self, PyObject *args) { - if (clock_gettime(clk_id, &ts) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } - return PyFloat_FromDouble((((double)ts.tv_nsec) / SEC_TO_NS) + (double)ts.tv_sec); -} -#endif - -static PyMethodDef monotonic_methods[] = { - {"monotonic", monotonic, METH_NOARGS, - "monotonic()\n\nReturn a monotonically increasing time value." - }, - - {NULL, NULL, 0, NULL} -}; - -CALIBRE_MODINIT_FUNC -initmonotonic(void) { - PyObject *m; -#ifdef _MSC_VER - /* if(!QueryPerformanceFrequency(&frequency)) { PyErr_SetFromWindowsErr(0); return; } */ -#endif -#ifdef __APPLE__ - mach_timebase_info(&timebase); -#endif - m = Py_InitModule3("monotonic", monotonic_methods, - "Implementation of time.monotonic() in C for speed" - ); - if (m == NULL) return; -} - diff --git a/src/calibre/utils/monotonic.py b/src/calibre/utils/monotonic.py index 1bb4f3fe72..5596878427 100644 --- a/src/calibre/utils/monotonic.py +++ b/src/calibre/utils/monotonic.py @@ -1,13 +1,4 @@ # vim:fileencoding=utf-8 - -try: - from time import monotonic -except ImportError: - from calibre.constants import plugins - - monotonicp, err = plugins['monotonic'] - if err: - raise RuntimeError('Failed to load the monotonic module with error: ' + err) - monotonic = monotonicp.monotonic - del monotonicp, err +from time import monotonic +monotonic diff --git a/src/calibre/utils/serialize.py b/src/calibre/utils/serialize.py index 1f9e0b38be..3de30016fa 100644 --- a/src/calibre/utils/serialize.py +++ b/src/calibre/utils/serialize.py @@ -2,10 +2,7 @@ # vim:fileencoding=utf-8 # License: GPLv3 Copyright: 2017, Kovid Goyal - - from polyglot.builtins import unicode_type -from calibre.constants import ispy3 MSGPACK_MIME = 'application/x-msgpack' @@ -118,22 +115,11 @@ def json_loads(data): return json.loads(data, object_hook=json_decoder) -if ispy3: +def pickle_dumps(data): + import pickle + return pickle.dumps(data, -1) - def pickle_dumps(data): - import pickle - return pickle.dumps(data, -1) - def pickle_loads(dump): - import pickle - return pickle.loads(dump, encoding='utf-8') - -else: - - def pickle_dumps(data): - import cPickle as pickle - return pickle.dumps(data, -1) - - def pickle_loads(dump): - import cPickle as pickle - return pickle.loads(dump) +def pickle_loads(dump): + import pickle + return pickle.loads(dump, encoding='utf-8') diff --git a/src/calibre/utils/shared_file.py b/src/calibre/utils/shared_file.py index 44aac391bf..441a60dd25 100644 --- a/src/calibre/utils/shared_file.py +++ b/src/calibre/utils/shared_file.py @@ -9,7 +9,7 @@ import os, sys from polyglot.builtins import reraise -from calibre.constants import iswindows, plugins, ispy3 +from calibre.constants import iswindows, plugins ''' This module defines a share_open() function which is a replacement for @@ -177,13 +177,7 @@ if iswindows: return speedup.fdopen(os_open(path, flags), path, mode, buffering) else: - if ispy3: - # See PEP 446 - share_open = open - else: - def share_open(path, mode='r', buffering=-1): - flags = flags_from_mode(mode) | speedup.O_CLOEXEC - return speedup.fdopen(os.open(path, flags), path, mode, buffering) + share_open = open def raise_winerror(x): reraise(NotImplementedError, None, sys.exc_info()[2]) diff --git a/src/calibre/utils/smtp.py b/src/calibre/utils/smtp.py index f945f361c9..b716099f5b 100644 --- a/src/calibre/utils/smtp.py +++ b/src/calibre/utils/smtp.py @@ -12,7 +12,7 @@ This module implements a simple commandline SMTP client that supports: import sys, traceback, os, socket, encodings.idna as idna from calibre import isbytestring -from calibre.constants import ispy3, iswindows +from calibre.constants import iswindows from polyglot.builtins import unicode_type, as_unicode, native_string_type @@ -150,7 +150,7 @@ def get_smtp_class(use_ssl=False, debuglevel=0): # but there is no way to set debuglevel before connect() is called import polyglot.smtplib as smtplib cls = smtplib.SMTP_SSL if use_ssl else smtplib.SMTP - bases = (cls,) if ispy3 else (cls, object) + bases = (cls,) return type(native_string_type('SMTP'), bases, {native_string_type('debuglevel'): debuglevel}) @@ -177,8 +177,6 @@ def sendmail(msg, from_, to, localhost=None, verbose=0, timeout=None, s.starttls(context=context) s.ehlo() if username is not None and password is not None: - if encryption == 'SSL' and not ispy3: - s.sock = s.file.sslobj s.login(username, password) ret = None try: diff --git a/src/calibre/utils/terminal.py b/src/calibre/utils/terminal.py index d945da9e56..ffbabcdc64 100644 --- a/src/calibre/utils/terminal.py +++ b/src/calibre/utils/terminal.py @@ -1,14 +1,13 @@ #!/usr/bin/env python # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai - __license__ = 'GPL v3' __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' import os, sys, re -from calibre.constants import iswindows, ispy3 +from calibre.constants import iswindows from polyglot.builtins import iteritems, range, zip, native_string_type if iswindows: @@ -150,12 +149,7 @@ class Detect(object): while text: t, text = text[:chunk], text[chunk:] wt = c_wchar_p(t) - if ispy3: - text_len = len(t.encode('utf-16')) - else: - # Use the fact that len(t) == wcslen(wt) in python 2.7 on - # windows where the python unicode type uses UTF-16 - text_len = len(t) + text_len = len(t.encode('utf-16')) if not self.write_console(self.file_handle, wt, text_len, byref(written), None): # Older versions of windows can fail to write large strings # to console with WriteConsoleW (seen it happen on Win XP) diff --git a/src/calibre/utils/unicode_getpass.py b/src/calibre/utils/unicode_getpass.py deleted file mode 100644 index 2c36ca9968..0000000000 --- a/src/calibre/utils/unicode_getpass.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# vim:fileencoding=utf-8 -# License: GPLv3 Copyright: 2017, Kovid Goyal - - - -import sys - -from calibre.constants import iswindows, preferred_encoding, ispy3 - - -if ispy3: - from getpass import getpass - getpass -else: - def getpass(prompt): - if iswindows: - # getpass is broken on windows with python 2.x and unicode, the - # below implementation is from the python 3 source code - import msvcrt - for c in prompt: - msvcrt.putwch(c) - pw = "" - while 1: - c = msvcrt.getwch() - if c == '\r' or c == '\n': - break - if c == '\003': - raise KeyboardInterrupt - if c == '\b': - pw = pw[:-1] - else: - pw = pw + c - msvcrt.putwch('\r') - msvcrt.putwch('\n') - return pw - else: - enc = getattr(sys.stdin, 'encoding', preferred_encoding) or preferred_encoding - from getpass import getpass - return getpass(prompt).decode(enc) diff --git a/src/calibre/utils/zlib2.c b/src/calibre/utils/zlib2.c deleted file mode 100644 index 77524a1558..0000000000 --- a/src/calibre/utils/zlib2.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * crc32.c - * Copyright (C) 2015 Kovid Goyal - * - * Distributed under terms of the GPL3 license. - */ - -#define UNICODE -#include -#include - -#define DEF_BUF_SIZE (16*1024) -/* The following parameters are copied from zutil.h, version 0.95 */ -#define DEFLATED 8 -#if MAX_MEM_LEVEL >= 8 -# define DEF_MEM_LEVEL 8 -#else -# define DEF_MEM_LEVEL MAX_MEM_LEVEL -#endif - - -static PyTypeObject Comptype; - -static PyObject *ZlibError = NULL; - -typedef struct -{ - PyObject_HEAD - z_stream zst; - PyObject *unused_data; - PyObject *unconsumed_tail; - char eof; - int is_initialised; - PyObject *zdict; -} compobject; - -static void -zlib_error(z_stream zst, int err, char *msg) -{ - const char *zmsg = Z_NULL; - /* In case of a version mismatch, zst.msg won't be initialized. - Check for this case first, before looking at zst.msg. */ - if (err == Z_VERSION_ERROR) - zmsg = "library version mismatch"; - if (zmsg == Z_NULL) - zmsg = zst.msg; - if (zmsg == Z_NULL) { - switch (err) { - case Z_BUF_ERROR: - zmsg = "incomplete or truncated stream"; - break; - case Z_STREAM_ERROR: - zmsg = "inconsistent stream state"; - break; - case Z_DATA_ERROR: - zmsg = "invalid input data"; - break; - } - } - if (zmsg == Z_NULL) - PyErr_Format(ZlibError, "Error %d %s", err, msg); - else - PyErr_Format(ZlibError, "Error %d %s: %.200s", err, msg, zmsg); -} - -static compobject * -newcompobject(PyTypeObject *type) -{ - compobject *self; - self = PyObject_New(compobject, type); - if (self == NULL) - return NULL; - self->eof = 0; - self->is_initialised = 0; - self->zdict = NULL; - self->unused_data = PyBytes_FromStringAndSize("", 0); - if (self->unused_data == NULL) { - Py_DECREF(self); - return NULL; - } - self->unconsumed_tail = PyBytes_FromStringAndSize("", 0); - if (self->unconsumed_tail == NULL) { - Py_DECREF(self); - return NULL; - } - return self; -} - - -static PyObject * -PyZlib_compressobj(PyObject *selfptr, PyObject *args) -{ - compobject *self = NULL; - int level=Z_DEFAULT_COMPRESSION, method=DEFLATED; - int wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL, strategy=Z_DEFAULT_STRATEGY, err; - - if (!PyArg_ParseTuple(args, "|iiiii:compressobj", &level, &method, &wbits, - &memLevel, &strategy)) - return NULL; - - self = newcompobject(&Comptype); - if (self==NULL) return NULL; - - self->zst.zalloc = (alloc_func)Z_NULL; - self->zst.zfree = (free_func)Z_NULL; - self->zst.next_in = Z_NULL; - self->zst.avail_in = 0; - err = deflateInit2(&self->zst, level, method, wbits, memLevel, strategy); - switch(err) { - case (Z_OK): - self->is_initialised = 1; - return (PyObject*)self; - case (Z_MEM_ERROR): - Py_DECREF(self); - PyErr_SetString(PyExc_MemoryError, - "Can't allocate memory for compression object"); - return NULL; - case(Z_STREAM_ERROR): - Py_DECREF(self); - PyErr_SetString(PyExc_ValueError, "Invalid initialization option"); - return NULL; - default: - zlib_error(self->zst, err, "while creating compression object"); - Py_DECREF(self); - return NULL; - } - return (PyObject*) self; -} - -static void -Dealloc(compobject *self) -{ - Py_XDECREF(self->unused_data); - Py_XDECREF(self->unconsumed_tail); - Py_XDECREF(self->zdict); - PyObject_Del(self); -} - -static void -Comp_dealloc(compobject *self) -{ - if (self->is_initialised) - deflateEnd(&self->zst); - Dealloc(self); -} - -static PyObject * -Compress_compress(compobject *self, PyObject *data_obj) -/*[clinic end generated code: output=5d5cd791cbc6a7f4 input=0d95908d6e64fab8]*/ -{ - int err = 0, len = 0; - unsigned int inplen = 0; - unsigned int length = DEF_BUF_SIZE, new_length = 0; - PyObject *RetVal = NULL; - Py_buffer indata = {0}; - Byte *input = NULL; - unsigned long start_total_out = 0; - - if (PyObject_GetBuffer(data_obj, &indata, PyBUF_SIMPLE) != 0) return NULL; - input = indata.buf; len = indata.len; - - if ((size_t)len > UINT_MAX) { - PyErr_SetString(PyExc_OverflowError, "Size does not fit in an unsigned int"); - goto done; - } - inplen = (unsigned int)len; - - if (!(RetVal = PyBytes_FromStringAndSize(NULL, length))) goto done; - - start_total_out = self->zst.total_out; - self->zst.avail_in = inplen; - self->zst.next_in = input; - self->zst.avail_out = length; - self->zst.next_out = (unsigned char *)PyBytes_AS_STRING(RetVal); - - Py_BEGIN_ALLOW_THREADS - err = deflate(&(self->zst), Z_NO_FLUSH); - Py_END_ALLOW_THREADS - - /* while Z_OK and the output buffer is full, there might be more output, - so extend the output buffer and try again */ - while (err == Z_OK && self->zst.avail_out == 0) { - if (length <= (UINT_MAX >> 1)) - new_length = length << 1; - else - new_length = UINT_MAX; - if (_PyBytes_Resize(&RetVal, new_length) < 0) { - Py_CLEAR(RetVal); - goto done; - } - self->zst.next_out = - (unsigned char *)PyBytes_AS_STRING(RetVal) + length; - self->zst.avail_out = length; - length = new_length; - - Py_BEGIN_ALLOW_THREADS - err = deflate(&(self->zst), Z_NO_FLUSH); - Py_END_ALLOW_THREADS - } - /* We will only get Z_BUF_ERROR if the output buffer was full but - there wasn't more output when we tried again, so it is not an error - condition. - */ - - if (err != Z_OK && err != Z_BUF_ERROR) { - zlib_error(self->zst, err, "while compressing data"); - Py_CLEAR(RetVal); - goto done; - } - if (_PyBytes_Resize(&RetVal, self->zst.total_out - start_total_out) < 0) { - Py_CLEAR(RetVal); - } - - done: - if (indata.obj) PyBuffer_Release(&indata); - return RetVal; -} - -static PyObject * -Compress_flush(compobject *self, PyObject *args) -{ - int err = 0, mode=Z_FINISH; - unsigned int length = DEF_BUF_SIZE, new_length = 0; - PyObject *RetVal = NULL; - unsigned long start_total_out = 0; - - if (!PyArg_ParseTuple(args, "|i:flush", &mode)) return NULL; - - /* Flushing with Z_NO_FLUSH is a no-op, so there's no point in - doing any work at all; just return an empty string. */ - if (mode == Z_NO_FLUSH) { - return PyBytes_FromStringAndSize(NULL, 0); - } - - if (!(RetVal = PyBytes_FromStringAndSize(NULL, length))) - return NULL; - - start_total_out = self->zst.total_out; - self->zst.avail_in = 0; - self->zst.avail_out = length; - self->zst.next_out = (unsigned char *)PyBytes_AS_STRING(RetVal); - - Py_BEGIN_ALLOW_THREADS - err = deflate(&(self->zst), mode); - Py_END_ALLOW_THREADS - - /* while Z_OK and the output buffer is full, there might be more output, - so extend the output buffer and try again */ - while (err == Z_OK && self->zst.avail_out == 0) { - if (length <= (UINT_MAX >> 1)) - new_length = length << 1; - else - new_length = UINT_MAX; - if (_PyBytes_Resize(&RetVal, new_length) < 0) { - Py_CLEAR(RetVal); - goto error; - } - self->zst.next_out = - (unsigned char *)PyBytes_AS_STRING(RetVal) + length; - self->zst.avail_out = length; - length = new_length; - - Py_BEGIN_ALLOW_THREADS - err = deflate(&(self->zst), mode); - Py_END_ALLOW_THREADS - } - - /* If mode is Z_FINISH, we also have to call deflateEnd() to free - various data structures. Note we should only get Z_STREAM_END when - mode is Z_FINISH, but checking both for safety*/ - if (err == Z_STREAM_END && mode == Z_FINISH) { - err = deflateEnd(&(self->zst)); - if (err != Z_OK) { - zlib_error(self->zst, err, "while finishing compression"); - Py_DECREF(RetVal); - RetVal = NULL; - goto error; - } - else - self->is_initialised = 0; - - /* We will only get Z_BUF_ERROR if the output buffer was full - but there wasn't more output when we tried again, so it is - not an error condition. - */ - } else if (err!=Z_OK && err!=Z_BUF_ERROR) { - zlib_error(self->zst, err, "while flushing"); - Py_DECREF(RetVal); - RetVal = NULL; - goto error; - } - - if (_PyBytes_Resize(&RetVal, self->zst.total_out - start_total_out) < 0) { - Py_CLEAR(RetVal); - } - - error: - - return RetVal; -} - -static PyMethodDef comp_methods[] = -{ - {"compress", (PyCFunction)Compress_compress, METH_O, "compress(data) -- returns compressed data, dont forget to call flush when done."}, - - {"flush", (PyCFunction)Compress_flush, METH_VARARGS, "flush([mode]) -- returns any remaining data"}, - - {NULL} -}; - -static PyTypeObject Comptype = { - PyVarObject_HEAD_INIT(0, 0) - "zlib2.Compress", - sizeof(compobject), - 0, - (destructor)Comp_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_reserved*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - comp_methods, /*tp_methods*/ -}; - - -static PyObject * -zlib_crc32(PyObject *self, PyObject *args) -{ - int signed_val = 0, len = 0; - unsigned int value = 0; - unsigned char *buf = NULL; - Py_buffer indata = {0}; - PyObject* obj = NULL; - - if(!PyArg_ParseTuple(args, "O|I:crc32", &obj, &value)) return NULL; - if (PyObject_GetBuffer(obj, &indata, PyBUF_SIMPLE) != 0) return NULL; - buf = indata.buf; len = indata.len; - - /* Releasing the GIL for very small buffers is inefficient - and may lower performance */ - if (len > 1024*5) { - Py_BEGIN_ALLOW_THREADS - /* Avoid truncation of length for very large buffers. crc32() takes - length as an unsigned int, which may be narrower than Py_ssize_t. */ - while ((size_t)len > UINT_MAX) { - value = crc32(value, buf, UINT_MAX); - buf += (size_t) UINT_MAX; - len -= (size_t) UINT_MAX; - } - signed_val = crc32(value, buf, (unsigned int)len); - Py_END_ALLOW_THREADS - } else { - signed_val = crc32(value, buf, len); - } - if (indata.obj) PyBuffer_Release(&indata); - return PyLong_FromUnsignedLong(signed_val & 0xffffffffU); -} - -static PyMethodDef methods[] = { - {"crc32", zlib_crc32, METH_VARARGS, - "crc32(data, [, state=0)\n\nCalculate crc32 for the given data starting from the given state." - }, - {"compressobj", (PyCFunction)PyZlib_compressobj, METH_VARARGS, "Create compression object"}, - - - {NULL, NULL, 0, NULL} -}; - -CALIBRE_MODINIT_FUNC -initzlib2(void) { - PyObject *m, *ver; - Comptype.tp_new = PyType_GenericNew; - if (PyType_Ready(&Comptype) < 0) - return; - - m = Py_InitModule3("zlib2", methods, - "Implementation of zlib compression with support for the buffer protocol, which is missing in Python2. Code taken from the Python3 zlib module" - ); - if (m == NULL) return; - PyModule_AddIntMacro(m, MAX_WBITS); - PyModule_AddIntMacro(m, DEFLATED); - PyModule_AddIntMacro(m, DEF_MEM_LEVEL); - PyModule_AddIntMacro(m, DEF_BUF_SIZE); - PyModule_AddIntMacro(m, Z_BEST_SPEED); - PyModule_AddIntMacro(m, Z_BEST_COMPRESSION); - PyModule_AddIntMacro(m, Z_DEFAULT_COMPRESSION); - PyModule_AddIntMacro(m, Z_FILTERED); - PyModule_AddIntMacro(m, Z_HUFFMAN_ONLY); - PyModule_AddIntMacro(m, Z_DEFAULT_STRATEGY); - - PyModule_AddIntMacro(m, Z_FINISH); - PyModule_AddIntMacro(m, Z_NO_FLUSH); - PyModule_AddIntMacro(m, Z_SYNC_FLUSH); - PyModule_AddIntMacro(m, Z_FULL_FLUSH); - - ver = PyUnicode_FromString(ZLIB_VERSION); - if (ver != NULL) - PyModule_AddObject(m, "ZLIB_VERSION", ver); - - ver = PyUnicode_FromString(zlibVersion()); - if (ver != NULL) - PyModule_AddObject(m, "ZLIB_RUNTIME_VERSION", ver); - - ZlibError = PyErr_NewException("zlib2.error", NULL, NULL); - if (ZlibError != NULL) { - Py_INCREF(ZlibError); - PyModule_AddObject(m, "error", ZlibError); - } -} diff --git a/src/polyglot/functools.py b/src/polyglot/functools.py index 68c5abe672..408f7b15c1 100644 --- a/src/polyglot/functools.py +++ b/src/polyglot/functools.py @@ -2,12 +2,6 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2019, Kovid Goyal - - -from polyglot.builtins import is_py3 -if is_py3: - from functools import lru_cache -else: - from backports.functools_lru_cache import lru_cache +from functools import lru_cache lru_cache diff --git a/src/polyglot/html_entities.py b/src/polyglot/html_entities.py index c006b2941f..c396e54615 100644 --- a/src/polyglot/html_entities.py +++ b/src/polyglot/html_entities.py @@ -2,9 +2,5 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2019, Eli Schwartz -from polyglot.builtins import is_py3 - -if is_py3: - from html.entities import name2codepoint -else: - from htmlentitydefs import name2codepoint +from html.entities import name2codepoint +name2codepoint diff --git a/src/polyglot/http_client.py b/src/polyglot/http_client.py index 3c6126337f..dbd2ccf3dd 100644 --- a/src/polyglot/http_client.py +++ b/src/polyglot/http_client.py @@ -2,21 +2,15 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2019, Eli Schwartz -from polyglot.builtins import is_py3 +from http.client import (responses, HTTPConnection, HTTPSConnection, + BAD_REQUEST, FOUND, FORBIDDEN, HTTP_VERSION_NOT_SUPPORTED, + INTERNAL_SERVER_ERROR, METHOD_NOT_ALLOWED, MOVED_PERMANENTLY, + NOT_FOUND, NOT_IMPLEMENTED, NOT_MODIFIED, OK, PARTIAL_CONTENT, + PRECONDITION_FAILED, REQUEST_ENTITY_TOO_LARGE, REQUEST_URI_TOO_LONG, + REQUESTED_RANGE_NOT_SATISFIABLE, REQUEST_TIMEOUT, SEE_OTHER, + SERVICE_UNAVAILABLE, UNAUTHORIZED, _CS_IDLE, _CS_REQ_SENT) -if is_py3: - from http.client import (responses, HTTPConnection, HTTPSConnection, - BAD_REQUEST, FOUND, FORBIDDEN, HTTP_VERSION_NOT_SUPPORTED, - INTERNAL_SERVER_ERROR, METHOD_NOT_ALLOWED, MOVED_PERMANENTLY, - NOT_FOUND, NOT_IMPLEMENTED, NOT_MODIFIED, OK, PARTIAL_CONTENT, - PRECONDITION_FAILED, REQUEST_ENTITY_TOO_LARGE, REQUEST_URI_TOO_LONG, - REQUESTED_RANGE_NOT_SATISFIABLE, REQUEST_TIMEOUT, SEE_OTHER, - SERVICE_UNAVAILABLE, UNAUTHORIZED, _CS_IDLE, _CS_REQ_SENT) -else: - from httplib import (responses, HTTPConnection, HTTPSConnection, - BAD_REQUEST, FOUND, FORBIDDEN, HTTP_VERSION_NOT_SUPPORTED, - INTERNAL_SERVER_ERROR, METHOD_NOT_ALLOWED, MOVED_PERMANENTLY, - NOT_FOUND, NOT_IMPLEMENTED, NOT_MODIFIED, OK, PARTIAL_CONTENT, - PRECONDITION_FAILED, REQUEST_ENTITY_TOO_LARGE, REQUEST_URI_TOO_LONG, - REQUESTED_RANGE_NOT_SATISFIABLE, REQUEST_TIMEOUT, SEE_OTHER, - SERVICE_UNAVAILABLE, UNAUTHORIZED, _CS_IDLE, _CS_REQ_SENT) +if False: + responses, HTTPConnection, HTTPSConnection, BAD_REQUEST, FOUND, FORBIDDEN, HTTP_VERSION_NOT_SUPPORTED, INTERNAL_SERVER_ERROR, METHOD_NOT_ALLOWED + MOVED_PERMANENTLY, NOT_FOUND, NOT_IMPLEMENTED, NOT_MODIFIED, OK, PARTIAL_CONTENT, PRECONDITION_FAILED, REQUEST_ENTITY_TOO_LARGE, REQUEST_URI_TOO_LONG + REQUESTED_RANGE_NOT_SATISFIABLE, REQUEST_TIMEOUT, SEE_OTHER, SERVICE_UNAVAILABLE, UNAUTHORIZED, _CS_IDLE, _CS_REQ_SENT diff --git a/src/polyglot/http_cookie.py b/src/polyglot/http_cookie.py index 1c5977f74f..5334fc3722 100644 --- a/src/polyglot/http_cookie.py +++ b/src/polyglot/http_cookie.py @@ -2,11 +2,5 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2019, Eli Schwartz -from polyglot.builtins import is_py3 - -if is_py3: - from http.cookies import SimpleCookie # noqa - from http.cookiejar import CookieJar, Cookie # noqa -else: - from Cookie import SimpleCookie # noqa - from cookielib import CookieJar, Cookie # noqa +from http.cookies import SimpleCookie # noqa +from http.cookiejar import CookieJar, Cookie # noqa diff --git a/src/polyglot/http_server.py b/src/polyglot/http_server.py index 544c86a065..ea0dff99c1 100644 --- a/src/polyglot/http_server.py +++ b/src/polyglot/http_server.py @@ -2,12 +2,4 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2018, Kovid Goyal - - -from polyglot.builtins import is_py3 - -if is_py3: - from http.server import HTTPServer, SimpleHTTPRequestHandler -else: - from BaseHTTPServer import HTTPServer # noqa - from SimpleHTTPServer import SimpleHTTPRequestHandler # noqa +from http.server import HTTPServer, SimpleHTTPRequestHandler # noqa diff --git a/src/polyglot/plistlib.py b/src/polyglot/plistlib.py index c4a38f9f20..b07f25765c 100644 --- a/src/polyglot/plistlib.py +++ b/src/polyglot/plistlib.py @@ -2,25 +2,10 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2019, Kovid Goyal +def unwrap_bytes(x): + return x +def wrap_bytes(x): + return x -from polyglot.builtins import is_py3 - -if is_py3: - from plistlib import loads, dumps # noqa - - def unwrap_bytes(x): - return x - - def wrap_bytes(x): - return x -else: - from plistlib import readPlistFromString as loads, writePlistToString as dumps, Data # noqa - - def unwrap_bytes(x): - if isinstance(x, Data): - x = x.data - return x - - def wrap_bytes(x): - return Data(x) +from plistlib import loads, dumps, Data # noqa diff --git a/src/polyglot/queue.py b/src/polyglot/queue.py index 51feafeaca..3239fc36c3 100644 --- a/src/polyglot/queue.py +++ b/src/polyglot/queue.py @@ -2,9 +2,4 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2019, Eli Schwartz -from polyglot.builtins import is_py3 - -if is_py3: - from queue import Queue, Empty, Full, PriorityQueue, LifoQueue # noqa -else: - from Queue import Queue, Empty, Full, PriorityQueue, LifoQueue # noqa +from queue import Queue, Empty, Full, PriorityQueue, LifoQueue # noqa diff --git a/src/polyglot/reprlib.py b/src/polyglot/reprlib.py index d3b745851e..d3e5869d7b 100644 --- a/src/polyglot/reprlib.py +++ b/src/polyglot/reprlib.py @@ -2,9 +2,5 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2019, Eli Schwartz -from polyglot.builtins import is_py3 - -if is_py3: - from reprlib import repr -else: - from repr import repr +from reprlib import repr +repr diff --git a/src/polyglot/smtplib.py b/src/polyglot/smtplib.py index cff16f9cf8..5457a416ab 100644 --- a/src/polyglot/smtplib.py +++ b/src/polyglot/smtplib.py @@ -2,13 +2,9 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2019, Kovid Goyal - - import sys from functools import partial -from polyglot.builtins import is_py3 - class FilteredLog(object): @@ -30,44 +26,30 @@ class FilteredLog(object): self.debug_to(*a) -if is_py3: - import smtplib +import smtplib - class SMTP(smtplib.SMTP): - def __init__(self, *a, **kw): - self.debug_to = FilteredLog(kw.pop('debug_to', None)) - super().__init__(*a, **kw) +class SMTP(smtplib.SMTP): - def _print_debug(self, *a): - if self.debug_to is not None: - self.debug_to(*a) - else: - super()._print_debug(*a) + def __init__(self, *a, **kw): + self.debug_to = FilteredLog(kw.pop('debug_to', None)) + super().__init__(*a, **kw) - class SMTP_SSL(smtplib.SMTP_SSL): + def _print_debug(self, *a): + if self.debug_to is not None: + self.debug_to(*a) + else: + super()._print_debug(*a) - def __init__(self, *a, **kw): - self.debug_to = FilteredLog(kw.pop('debug_to', None)) - super().__init__(*a, **kw) - def _print_debug(self, *a): - if self.debug_to is not None: - self.debug_to(*a) - else: - super()._print_debug(*a) +class SMTP_SSL(smtplib.SMTP_SSL): -else: - import calibre.utils.smtplib as smtplib + def __init__(self, *a, **kw): + self.debug_to = FilteredLog(kw.pop('debug_to', None)) + super().__init__(*a, **kw) - class SMTP(smtplib.SMTP): - - def __init__(self, *a, **kw): - kw['debug_to'] = FilteredLog(kw.get('debug_to')) - smtplib.SMTP.__init__(self, *a, **kw) - - class SMTP_SSL(smtplib.SMTP_SSL): - - def __init__(self, *a, **kw): - kw['debug_to'] = FilteredLog(kw.get('debug_to')) - smtplib.SMTP_SSL.__init__(self, *a, **kw) + def _print_debug(self, *a): + if self.debug_to is not None: + self.debug_to(*a) + else: + super()._print_debug(*a) diff --git a/src/polyglot/socketserver.py b/src/polyglot/socketserver.py index da5c644531..9be3c51e03 100644 --- a/src/polyglot/socketserver.py +++ b/src/polyglot/socketserver.py @@ -2,9 +2,4 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2019, Eli Schwartz -from polyglot.builtins import is_py3 - -if is_py3: - from socketserver import TCPServer, ThreadingMixIn # noqa -else: - from SocketServer import TCPServer, ThreadingMixIn # noqa +from socketserver import TCPServer, ThreadingMixIn # noqa diff --git a/src/polyglot/urllib.py b/src/polyglot/urllib.py index 08684dd17b..1a128d47b6 100644 --- a/src/polyglot/urllib.py +++ b/src/polyglot/urllib.py @@ -4,46 +4,22 @@ -from polyglot.builtins import is_py3 +from urllib.request import (build_opener, getproxies, install_opener, # noqa + HTTPBasicAuthHandler, HTTPCookieProcessor, HTTPDigestAuthHandler, # noqa + url2pathname, urlopen, Request) # noqa +from urllib.parse import (parse_qs, quote, unquote as uq, quote_plus, urldefrag, # noqa + urlencode, urljoin, urlparse, urlunparse, urlsplit, urlunsplit) # noqa +from urllib.error import HTTPError, URLError # noqa -if is_py3: - from urllib.request import (build_opener, getproxies, install_opener, # noqa - HTTPBasicAuthHandler, HTTPCookieProcessor, HTTPDigestAuthHandler, # noqa - url2pathname, urlopen, Request) # noqa - from urllib.parse import (parse_qs, quote, unquote as uq, quote_plus, urldefrag, # noqa - urlencode, urljoin, urlparse, urlunparse, urlsplit, urlunsplit) # noqa - from urllib.error import HTTPError, URLError # noqa - def unquote(x, encoding='utf-8', errors='replace'): - binary = isinstance(x, bytes) - if binary: - x = x.decode(encoding, errors) - ans = uq(x, encoding, errors) - if binary: - ans = ans.encode(encoding, errors) - return ans - -else: - from urllib import (getproxies, quote, unquote as uq, quote_plus, url2pathname, # noqa - urlencode) # noqa - from urllib2 import (build_opener, install_opener, HTTPBasicAuthHandler, # noqa - HTTPCookieProcessor, HTTPDigestAuthHandler, HTTPError, URLError, # noqa - urlopen, Request) # noqa - from urlparse import (parse_qs, urldefrag, urljoin, urlparse, urlunparse, # noqa - urlsplit, urlunsplit) # noqa - - def unquote(x, encoding='utf-8', errors='replace'): - # unquote must run on a bytestring and will return a bytestring - # If it runs on a unicode object, it returns a double encoded unicode - # string: unquote(u'%C3%A4') != unquote(b'%C3%A4').decode('utf-8') - # and the latter is correct - binary = isinstance(x, bytes) - if not binary: - x = x.encode(encoding, errors) - ans = uq(x) - if not binary: - ans = ans.decode(encoding, errors) - return ans +def unquote(x, encoding='utf-8', errors='replace'): + binary = isinstance(x, bytes) + if binary: + x = x.decode(encoding, errors) + ans = uq(x, encoding, errors) + if binary: + ans = ans.encode(encoding, errors) + return ans def unquote_plus(x, encoding='utf-8', errors='replace'):