diff --git a/emails/__init__.py b/emails/__init__.py index 6a94adc..b4f29e6 100644 --- a/emails/__init__.py +++ b/emails/__init__.py @@ -35,7 +35,6 @@ """ -from __future__ import unicode_literals __title__ = 'emails' __version__ = '0.6' diff --git a/emails/backend/inmemory/__init__.py b/emails/backend/inmemory/__init__.py index 6a39406..5f24697 100644 --- a/emails/backend/inmemory/__init__.py +++ b/emails/backend/inmemory/__init__.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals __all__ = ['InMemoryBackend', ] diff --git a/emails/backend/smtp/backend.py b/emails/backend/smtp/backend.py index 9fde8e8..e1cf989 100644 --- a/emails/backend/smtp/backend.py +++ b/emails/backend/smtp/backend.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals import smtplib import logging from functools import wraps diff --git a/emails/backend/smtp/exceptions.py b/emails/backend/smtp/exceptions.py index df19e93..a747aec 100644 --- a/emails/backend/smtp/exceptions.py +++ b/emails/backend/smtp/exceptions.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals import socket diff --git a/emails/compat/__init__.py b/emails/compat/__init__.py deleted file mode 100644 index d4a10f8..0000000 --- a/emails/compat/__init__.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -""" -pythoncompat -""" - -from .orderedset import OrderedSet - -#from . import _urlparse as urlparse - - -import sys - -# ------- -# Pythons -# ------- - -# Syntax sugar. -_ver = sys.version_info - -#: Python 2.x? -is_py2 = (_ver[0] == 2) - -#: Python 3.x? -is_py3 = (_ver[0] == 3) - -#: Python 3.0.x -is_py30 = (is_py3 and _ver[1] == 0) - -#: Python 3.1.x -is_py31 = (is_py3 and _ver[1] == 1) - -#: Python 3.2.x -is_py32 = (is_py3 and _ver[1] == 2) - -#: Python 3.3.x -is_py33 = (is_py3 and _ver[1] == 3) - -#: Python 3.4.x -is_py34 = (is_py3 and _ver[1] == 4) -is_py34_plus = (is_py3 and _ver[1] >= 4) - -#: Python 2.7.x -is_py27 = (is_py2 and _ver[1] == 7) - -#: Python 2.6.x -is_py26 = (is_py2 and _ver[1] == 6) - -#: Python 2.5.x -is_py25 = (is_py2 and _ver[1] == 5) - -#: Python 2.4.x -is_py24 = (is_py2 and _ver[1] == 4) - - -# --------- -# Platforms -# --------- - - -# Syntax sugar. -_ver = sys.version.lower() - -is_pypy = ('pypy' in _ver) -is_jython = ('jython' in _ver) -is_ironpython = ('iron' in _ver) - -# Assume CPython, if nothing else. -is_cpython = not any((is_pypy, is_jython, is_ironpython)) - -# Windows-based system. -is_windows = 'win32' in str(sys.platform).lower() - -# Standard Linux 2+ system. -is_linux = ('linux' in str(sys.platform).lower()) -is_osx = ('darwin' in str(sys.platform).lower()) -is_hpux = ('hpux' in str(sys.platform).lower()) # Complete guess. -is_solaris = ('solar==' in str(sys.platform).lower()) # Complete guess. - -# --------- -# Specifics -# --------- - -if is_py2: - - unichr = unichr - text_type = unicode - string_types = (str, unicode) - integer_types = (int, long) - int_to_byte = chr - - import urlparse - from .ordereddict import OrderedDict - - from StringIO import StringIO - from cStringIO import StringIO as BytesIO - NativeStringIO = BytesIO - - def to_native(x, charset=sys.getdefaultencoding(), errors='strict'): - if x is None or isinstance(x, str): - return x - return x.encode(charset, errors) - - def is_callable(x): - return callable(x) - - def to_bytes(x, charset=sys.getdefaultencoding(), errors='strict'): - if x is None: - return None - if isinstance(x, (bytes, bytearray, buffer)): - return bytes(x) - if isinstance(x, unicode): - return x.encode(charset, errors) - raise TypeError('Expected bytes') - - from email.utils import formataddr - - -elif is_py3: - import urllib.parse as urlparse - - try: - from collections.abc import Callable - except ImportError: - from collections import Callable - - from collections import OrderedDict - - from io import StringIO, BytesIO - NativeStringIO = StringIO - - unichr = chr - text_type = str - string_types = (str, ) - integer_types = (int, ) - - def to_native(x, charset=sys.getdefaultencoding(), errors='strict'): - if x is None or isinstance(x, str): - return x - return x.decode(charset, errors) - - def is_callable(x): - return isinstance(x, Callable) - - def to_bytes(x, charset=sys.getdefaultencoding(), errors='strict'): - if x is None: - return None - if isinstance(x, (bytes, bytearray, memoryview)): - return bytes(x) - if isinstance(x, str): - return x.encode(charset, errors) - raise TypeError('Expected bytes') - - from email.utils import escapesre, specialsre - - def formataddr(pair): - """ - This code is copy of python2 email.utils.formataddr. - Takes a 2-tuple of the form (realname, email_address) and returns RFC2822-like string. - Does not encode non-ascii realname. - - Python3 email.utils.formataddr do encode realname. - - TODO: switch to email.headerregistry.AddressHeader ? - """ - - name, address = pair - if name: - quotes = '' - if specialsre.search(name): - quotes = '"' - name = escapesre.sub(r'\\\g<0>', name) - return '%s%s%s <%s>' % (quotes, name, quotes, address) - return address - - -def to_unicode(x, charset=sys.getdefaultencoding(), errors='strict', - allow_none_charset=False): - if x is None: - return None - if not isinstance(x, bytes): - return text_type(x) - if charset is None and allow_none_charset: - return x - return x.decode(charset, errors) \ No newline at end of file diff --git a/emails/compat/_urlparse.py b/emails/compat/_urlparse.py deleted file mode 100644 index 378fab6..0000000 --- a/emails/compat/_urlparse.py +++ /dev/null @@ -1,6 +0,0 @@ -# encoding: utf-8 - -try: - from urlparse import * -except ImportError: - from urllib.parse import * diff --git a/emails/compat/ordereddict.py b/emails/compat/ordereddict.py deleted file mode 100644 index eefdac6..0000000 --- a/emails/compat/ordereddict.py +++ /dev/null @@ -1,264 +0,0 @@ -# encoding: utf-8 -from __future__ import unicode_literals -__all__ = ['OrderedDict', ] - -# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. -# Passes Python2.7's test suite and incorporates all the latest updates. -# Copyright 2009 Raymond Hettinger, released under the MIT License. -# http://code.activestate.com/recipes/576693/ - -try: - from thread import get_ident as _get_ident -except ImportError: - from dummy_thread import get_ident as _get_ident - -try: - from _abcoll import KeysView, ValuesView, ItemsView -except ImportError: - pass - - -class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as for regular dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link which goes at the end of the linked - # list, and the inherited dictionary is updated with the new key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link_prev, link_next, key = self.__map.pop(key) - link_prev[1] = link_next - link_next[0] = link_prev - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - root = self.__root - curr = root[1] - while curr is not root: - yield curr[2] - curr = curr[1] - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - root = self.__root - curr = root[0] - while curr is not root: - yield curr[2] - curr = curr[0] - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.itervalues(): - del node[:] - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - ''' - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root[0] - link_prev = link[0] - link_prev[1] = root - root[0] = link_prev - else: - link = root[1] - link_next = link[1] - root[1] = link_next - link_next[0] = root - key = link[2] - del self.__map[key] - value = dict.pop(self, key) - return key, value - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - 'od.keys() -> list of keys in od' - return list(self) - - def values(self): - 'od.values() -> list of values in od' - return [self[key] for key in self] - - def items(self): - 'od.items() -> list of (key, value) pairs in od' - return [(key, self[key]) for key in self] - - def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' - return iter(self) - - def itervalues(self): - 'od.itervalues -> an iterator over the values in od' - for k in self: - yield self[k] - - def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) items in od' - for k in self: - yield (k, self[k]) - - def update(*args, **kwds): - '''od.update(E, **F) -> None. Update od from dict/iterable E and F. - - If E is a dict instance, does: for k in E: od[k] = E[k] - If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] - Or if E is an iterable of items, does: for k, v in E: od[k] = v - In either case, this is followed by: for k, v in F.items(): od[k] = v - - ''' - if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) - elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') - self = args[0] - # Make progressively weaker assumptions about "other" - other = () - if len(args) == 2: - other = args[1] - if isinstance(other, dict): - for key in other: - self[key] = other[key] - elif hasattr(other, 'keys'): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in kwds.items(): - self[key] = value - - __update = update # let subclasses override update without breaking __init__ - - __marker = object() - - def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - - ''' - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - def __repr__(self, _repr_running={}): - 'od.__repr__() <==> repr(od)' - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - finally: - del _repr_running[call_key] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - ''' - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other - - # -- the following methods are only used in Python 2.7 -- - - def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) - - def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) - - def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) \ No newline at end of file diff --git a/emails/compat/orderedset.py b/emails/compat/orderedset.py deleted file mode 100644 index e75dc23..0000000 --- a/emails/compat/orderedset.py +++ /dev/null @@ -1,70 +0,0 @@ -# encoding: utf-8 -from __future__ import unicode_literals -__all__ = ['OrderedSet', ] - -try: - from collections.abc import MutableSet -except ImportError: - from collections import MutableSet - - -# http://code.activestate.com/recipes/576694/ - - -class OrderedSet(MutableSet): - - def __init__(self, iterable=None): - self.end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.map = {} # key --> [key, prev, next] - if iterable is not None: - self |= iterable - - def __len__(self): - return len(self.map) - - def __contains__(self, key): - return key in self.map - - def add(self, key): - if key not in self.map: - end = self.end - curr = end[1] - curr[2] = end[1] = self.map[key] = [key, curr, end] - - def discard(self, key): - if key in self.map: - key, prev, next = self.map.pop(key) - prev[2] = next - next[1] = prev - - def __iter__(self): - end = self.end - curr = end[2] - while curr is not end: - yield curr[0] - curr = curr[2] - - def __reversed__(self): - end = self.end - curr = end[1] - while curr is not end: - yield curr[0] - curr = curr[1] - - def pop(self, last=True): - if not self: - raise KeyError('set is empty') - key = self.end[1][0] if last else self.end[2][0] - self.discard(key) - return key - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, list(self)) - - def __eq__(self, other): - if isinstance(other, OrderedSet): - return len(self) == len(other) and list(self) == list(other) - return set(self) == set(other) diff --git a/emails/django/__init__.py b/emails/django/__init__.py index 78832df..1594643 100644 --- a/emails/django/__init__.py +++ b/emails/django/__init__.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import absolute_import from django.core.mail import get_connection from .. message import MessageTransformerMixin, MessageSignMixin, MessageBuildMixin, BaseMessage from .. utils import sanitize_email diff --git a/emails/loader/__init__.py b/emails/loader/__init__.py index 3e2767d..eb8ca3a 100644 --- a/emails/loader/__init__.py +++ b/emails/loader/__init__.py @@ -2,8 +2,9 @@ import os.path from email.utils import formataddr -from ..compat import to_unicode, to_native -from ..compat import urlparse +import urllib.parse as urlparse + +from ..utils import to_unicode, to_native from ..message import Message from ..utils import fetch_url from .local_store import (FileSystemLoader, ZipLoader, MsgLoader, FileNotFound) diff --git a/emails/loader/helpers.py b/emails/loader/helpers.py index 73578ec..7147417 100644 --- a/emails/loader/helpers.py +++ b/emails/loader/helpers.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals __all__ = ['guess_charset', 'fix_content_type'] from email.message import Message @@ -14,7 +13,7 @@ except ImportError: import chardet -from ..compat import to_native, to_unicode +from ..utils import to_native, to_unicode # HTML page charset stuff diff --git a/emails/loader/local_store.py b/emails/loader/local_store.py index 1d1cbeb..646f783 100644 --- a/emails/loader/local_store.py +++ b/emails/loader/local_store.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals from email.utils import parseaddr import logging import mimetypes @@ -10,11 +9,9 @@ from zipfile import ZipFile import email -from ..compat import to_unicode, string_types, to_native, formataddr as compat_formataddr - +from ..utils import to_unicode, to_native, formataddr, decode_header from ..loader.helpers import decode_text from ..message import Message -from ..utils import decode_header class FileNotFound(Exception): pass @@ -128,7 +125,7 @@ class FileSystemLoader(BaseLoader): """ def __init__(self, searchpath, encoding='utf-8', base_path=None): - if isinstance(searchpath, string_types): + if isinstance(searchpath, str): searchpath = [searchpath] self.searchpath = list(searchpath) self.encoding = encoding @@ -230,7 +227,7 @@ class MsgLoader(BaseLoader): common_charsets = ['ascii', 'utf-8', 'utf-16', 'windows-1252', 'cp850', 'windows-1251'] def __init__(self, msg, base_path=None): - if isinstance(msg, string_types): + if isinstance(msg, str): self.msg = email.message_from_string(msg) elif isinstance(msg, bytes): self.msg = email.message_from_string(to_native(msg)) @@ -367,7 +364,7 @@ def decode_address_header_value(self, value, skip_invalid=False): if not skip_invalid: r.append(decode_header(email)) else: - r.append(compat_formataddr([decode_header(name), email])) + r.append(formataddr([decode_header(name), email])) return r diff --git a/emails/message.py b/emails/message.py index 341a70f..ab0ae1c 100644 --- a/emails/message.py +++ b/emails/message.py @@ -1,10 +1,9 @@ # coding: utf-8 -from __future__ import unicode_literals from email.utils import getaddresses -from .compat import (string_types, is_callable, formataddr as compat_formataddr, to_unicode, to_native) -from .utils import (SafeMIMEText, SafeMIMEMultipart, sanitize_address, +from .utils import (formataddr, to_unicode, to_native, + SafeMIMEText, SafeMIMEMultipart, sanitize_address, parse_name_and_email, load_email_charsets, encode_header as encode_header_, renderable, format_date_header, parse_name_and_email_list, @@ -157,9 +156,9 @@ def get_date(self): v = self._date if v is False: return None - if is_callable(v): + if callable(v): v = v() - if not isinstance(v, string_types): + if not isinstance(v, str): v = format_date_header(v) return v @@ -171,7 +170,7 @@ def message_id(self): mid = self._message_id if mid is False: return None - return is_callable(mid) and mid() or mid + return callable(mid) and mid() or mid @message_id.setter def message_id(self, value): @@ -211,7 +210,7 @@ def encode_address_header(self, pair): if not pair: return None name, email = pair - return compat_formataddr((name or '', email)) + return formataddr((name or '', email)) encode_name_header = encode_address_header # legacy name @@ -221,7 +220,7 @@ def set_header(self, msg, key, value, encode=True): # TODO: may be remove header here ? return - if not isinstance(value, string_types): + if not isinstance(value, str): value = to_unicode(value) # Prevent header injection diff --git a/emails/signers.py b/emails/signers.py index e261bed..4ab52d2 100644 --- a/emails/signers.py +++ b/emails/signers.py @@ -5,13 +5,12 @@ # - use dkimpy v0.3 from http://hewgill.com/pydkim/ # - install hashlib (https://pypi.python.org/pypi/hashlib/20081119) and dnspython -from __future__ import unicode_literals import logging from .packages import dkim from .packages.dkim import DKIMException, UnparsableKeyError from .packages.dkim.crypto import parse_pem_private_key -from .compat import to_bytes, to_native +from .utils import to_bytes, to_native class DKIMSigner: diff --git a/emails/store/file.py b/emails/store/file.py index 4a9d26a..080ffbb 100644 --- a/emails/store/file.py +++ b/emails/store/file.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals import uuid from mimetypes import guess_type @@ -7,8 +6,9 @@ from email.encoders import encode_base64 from os.path import basename -from ..compat import urlparse, string_types, to_bytes -from ..utils import fetch_url, encode_header +import urllib.parse as urlparse + +from ..utils import fetch_url, encode_header, to_bytes MIMETYPE_UNKNOWN = 'application/unknown' @@ -51,7 +51,7 @@ def as_dict(self, fields=None): def get_data(self): _data = getattr(self, '_data', None) - if isinstance(_data, string_types): + if isinstance(_data, str): return _data elif hasattr(_data, 'read'): return _data.read() diff --git a/emails/store/store.py b/emails/store/store.py index d43b04c..60fd63a 100644 --- a/emails/store/store.py +++ b/emails/store/store.py @@ -1,8 +1,8 @@ # encoding: utf-8 -from __future__ import unicode_literals from os.path import splitext -from ..compat import OrderedDict, string_types +from collections import OrderedDict + from .file import BaseFile @@ -23,7 +23,7 @@ def __init__(self, file_cls=None): def __contains__(self, k): if isinstance(k, self.file_cls): return k.uri in self._files - elif isinstance(k, string_types): + elif isinstance(k, str): return k in self._files else: return False @@ -42,7 +42,7 @@ def remove(self, uri): if isinstance(uri, self.file_cls): uri = uri.uri - assert isinstance(uri, string_types) + assert isinstance(uri, str) v = self[uri] if v: diff --git a/emails/template/base.py b/emails/template/base.py index b80d57d..7b9976c 100644 --- a/emails/template/base.py +++ b/emails/template/base.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals import string diff --git a/emails/template/jinja_template.py b/emails/template/jinja_template.py index 8f92f37..8a31d88 100644 --- a/emails/template/jinja_template.py +++ b/emails/template/jinja_template.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals from .base import BaseTemplate diff --git a/emails/template/mako_template.py b/emails/template/mako_template.py index f2f497d..c8b7208 100644 --- a/emails/template/mako_template.py +++ b/emails/template/mako_template.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals from .base import BaseTemplate diff --git a/emails/testsuite/conftest.py b/emails/testsuite/conftest.py index 60c93c8..3fb2949 100644 --- a/emails/testsuite/conftest.py +++ b/emails/testsuite/conftest.py @@ -8,7 +8,6 @@ import sys import platform -from emails.compat import to_native, is_py3, to_unicode logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger() diff --git a/emails/testsuite/django_/test_django_integrations.py b/emails/testsuite/django_/test_django_integrations.py index 8ec8031..f8e43bb 100644 --- a/emails/testsuite/django_/test_django_integrations.py +++ b/emails/testsuite/django_/test_django_integrations.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals import warnings import emails import emails.message diff --git a/emails/testsuite/loader/test_helpers.py b/emails/testsuite/loader/test_helpers.py index 9c7eea7..ab24b2a 100644 --- a/emails/testsuite/loader/test_helpers.py +++ b/emails/testsuite/loader/test_helpers.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals, print_function import logging; import cssutils; cssutils.log.setLevel(logging.FATAL) diff --git a/emails/testsuite/loader/test_loaders.py b/emails/testsuite/loader/test_loaders.py index 1f2dbdf..958574a 100644 --- a/emails/testsuite/loader/test_loaders.py +++ b/emails/testsuite/loader/test_loaders.py @@ -1,5 +1,4 @@ # encoding: utf-8 -from __future__ import unicode_literals, print_function import os from lxml.etree import XMLSyntaxError import pytest @@ -10,7 +9,6 @@ import emails.transformer from emails.loader.local_store import (MsgLoader, FileSystemLoader, FileNotFound, ZipLoader, split_template_path, BaseLoader) -from emails.compat import text_type, is_pypy from emails.loader.helpers import guess_charset from emails.exc import HTTPLoaderError @@ -178,14 +176,7 @@ def test_external_urls(): # Skip if external site does responds 500 pass except SystemError: - if is_pypy and os.environ.get('TRAVIS'): - # pypy on travis-ci raises SystemError/StackOverflow - # in lxml xpath expression for [very complex] smashingmagazine.com html - # Think this is not critical. - # And I can't reproduce this locally, so just ignore it. - pass - else: - raise + raise assert success # one of urls should work I hope @@ -199,7 +190,7 @@ def _get_loaders(): def test_local_store1(): for loader in _get_loaders(): print(loader) - assert isinstance(loader.content('index.html'), text_type) + assert isinstance(loader.content('index.html'), str) assert isinstance(loader['index.html'], bytes) assert '', name) + return '%s%s%s <%s>' % (quotes, name, quotes, address) + return address + + _charsets_loaded = False CHARSETS_FIX = [ @@ -120,7 +161,7 @@ def parse_name_and_email_list(elements, encoding='utf-8'): if not elements: return [] - if isinstance(elements, string_types): + if isinstance(elements, str): return [parse_name_and_email(elements, encoding), ] if not isinstance(elements, (list, tuple)): @@ -131,7 +172,7 @@ def parse_name_and_email_list(elements, encoding='utf-8'): # Let's do some guesses if isinstance(elements, tuple): n, e = elements - if isinstance(e, string_types) and (not n or isinstance(n, string_types)): + if isinstance(e, str) and (not n or isinstance(n, str)): # It is probably a pair (name, email) return [parse_name_and_email(elements, encoding), ] @@ -147,7 +188,7 @@ def parse_name_and_email(obj, encoding='utf-8'): name, email = obj else: raise ValueError("Can not parse_name_and_email from %s" % obj) - elif isinstance(obj, string_types): + elif isinstance(obj, str): name, email = parseaddr(obj) else: raise ValueError("Can not parse_name_and_email from %s" % obj) @@ -172,7 +213,7 @@ def sanitize_email(addr, encoding='ascii', parse=False): def sanitize_address(addr, encoding='ascii'): - if isinstance(addr, string_types): + if isinstance(addr, str): addr = parseaddr(to_unicode(addr)) nm, addr = addr # This try-except clause is needed on Python 3 < 3.2.4 @@ -192,19 +233,13 @@ def as_string(self, unixfrom=False, linesep='\n'): This overrides the default as_string() implementation to not mangle lines that begin with 'From '. See bug #13433 for details. """ - fp = NativeStringIO() + fp = StringIO() g = generator.Generator(fp, mangle_from_=False) - if is_py2: - g.flatten(self, unixfrom=unixfrom) - else: - g.flatten(self, unixfrom=unixfrom, linesep=linesep) + g.flatten(self, unixfrom=unixfrom, linesep=linesep) return fp.getvalue() - if is_py2: - as_bytes = as_string - else: - def as_bytes(self, unixfrom=False, linesep='\n'): + def as_bytes(self, unixfrom=False, linesep='\n'): """Return the entire formatted message as bytes. Optional `unixfrom' when True, means include the Unix From_ envelope header. @@ -245,7 +280,7 @@ def fetch_url(url, valid_http_codes=(200, ), requests_args=None): def encode_header(value, charset='utf-8'): - if isinstance(value, string_types): + if isinstance(value, str): value = to_unicode(value, charset=charset).rstrip() _r = Header(value, charset) return str(_r) diff --git a/setup.cfg b/setup.cfg index 73c0a4a..741e83c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,3 +5,9 @@ universal=1 norecursedirs = .* {arch} *.egg *.egg-info dist build requirements markers = e2e: tests that require a running SMTP server + +[coverage:run] +omit = emails/testsuite/* + +[coverage:report] +omit = emails/testsuite/* diff --git a/setup.py b/setup.py index 442de22..216c982 100644 --- a/setup.py +++ b/setup.py @@ -116,7 +116,6 @@ def find_version(*file_paths): author_email='s@lavr.me', url='https://github.com/lavr/python-emails', packages=['emails', - 'emails.compat', 'emails.django', 'emails.loader', 'emails.store',