Merge pull request #1743 from fonttools/remove-some-py2-vestiges
Remove some Python 2 vestiges, deprecate using non-builtin types in plistlib
This commit is contained in:
commit
f939d2dc31
@ -7,16 +7,9 @@ import sys
|
|||||||
import logging
|
import logging
|
||||||
import timeit
|
import timeit
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
try:
|
from collections.abc import Mapping, Callable
|
||||||
from collections.abc import Mapping, Callable
|
|
||||||
except ImportError: # python < 3.3
|
|
||||||
from collections import Mapping, Callable
|
|
||||||
import warnings
|
import warnings
|
||||||
|
from logging import PercentStyle
|
||||||
try:
|
|
||||||
from logging import PercentStyle
|
|
||||||
except ImportError:
|
|
||||||
PercentStyle = None
|
|
||||||
|
|
||||||
|
|
||||||
# default logging level used by Timer class
|
# default logging level used by Timer class
|
||||||
|
@ -3,10 +3,7 @@ from fontTools.misc.py23 import *
|
|||||||
import struct
|
import struct
|
||||||
from fontTools.misc import sstruct
|
from fontTools.misc import sstruct
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
try:
|
from collections.abc import MutableMapping
|
||||||
from collections.abc import MutableMapping
|
|
||||||
except ImportError:
|
|
||||||
from UserDict import DictMixin as MutableMapping
|
|
||||||
|
|
||||||
|
|
||||||
class ResourceError(Exception):
|
class ResourceError(Exception):
|
||||||
|
@ -1,49 +1,35 @@
|
|||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
|
import warnings
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from base64 import b64encode, b64decode
|
from base64 import b64encode, b64decode
|
||||||
from numbers import Integral
|
from numbers import Integral
|
||||||
|
|
||||||
try:
|
from types import SimpleNamespace
|
||||||
from collections.abc import Mapping # python >= 3.3
|
from collections.abc import Mapping
|
||||||
except ImportError:
|
from functools import singledispatch
|
||||||
from collections import Mapping
|
|
||||||
|
|
||||||
try:
|
|
||||||
from functools import singledispatch
|
|
||||||
except ImportError:
|
|
||||||
try:
|
|
||||||
from singledispatch import singledispatch
|
|
||||||
except ImportError:
|
|
||||||
singledispatch = None
|
|
||||||
|
|
||||||
from fontTools.misc import etree
|
from fontTools.misc import etree
|
||||||
|
|
||||||
from fontTools.misc.py23 import (
|
from fontTools.misc.py23 import (
|
||||||
unicode,
|
|
||||||
basestring,
|
|
||||||
tounicode,
|
tounicode,
|
||||||
tobytes,
|
tobytes,
|
||||||
SimpleNamespace,
|
|
||||||
range,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# On python3, by default we deserialize <data> elements as bytes, whereas on
|
# By default, we
|
||||||
# python2 we deserialize <data> elements as plistlib.Data objects, in order
|
# - deserialize <data> elements as bytes and
|
||||||
# to distinguish them from the built-in str type (which is bytes on python2).
|
# - serialize bytes as <data> elements.
|
||||||
# Similarly, by default on python3 we serialize bytes as <data> elements;
|
# Before, on Python 2, we
|
||||||
# however, on python2 we serialize bytes as <string> elements (they must
|
# - deserialized <data> elements as plistlib.Data objects, in order to
|
||||||
# only contain ASCII characters in this case).
|
# distinguish them from the built-in str type (which is bytes on python2)
|
||||||
# You can pass use_builtin_types=[True|False] to load/dump etc. functions to
|
# - serialized bytes as <string> elements (they must have only contained
|
||||||
# enforce the same treatment of bytes across python 2 and 3.
|
# ASCII characters in this case)
|
||||||
|
# You can pass use_builtin_types=[True|False] to the load/dump etc. functions
|
||||||
|
# to enforce a specific treatment.
|
||||||
# NOTE that unicode type always maps to <string> element, and plistlib.Data
|
# NOTE that unicode type always maps to <string> element, and plistlib.Data
|
||||||
# always maps to <data> element, regardless of use_builtin_types.
|
# always maps to <data> element, regardless of use_builtin_types.
|
||||||
PY3 = sys.version_info[0] > 2
|
USE_BUILTIN_TYPES = True
|
||||||
if PY3:
|
|
||||||
USE_BUILTIN_TYPES = True
|
|
||||||
else:
|
|
||||||
USE_BUILTIN_TYPES = False
|
|
||||||
|
|
||||||
XML_DECLARATION = b"""<?xml version='1.0' encoding='UTF-8'?>"""
|
XML_DECLARATION = b"""<?xml version='1.0' encoding='UTF-8'?>"""
|
||||||
|
|
||||||
@ -62,7 +48,7 @@ _date_parser = re.compile(
|
|||||||
r"(?::(?P<minute>\d\d)"
|
r"(?::(?P<minute>\d\d)"
|
||||||
r"(?::(?P<second>\d\d))"
|
r"(?::(?P<second>\d\d))"
|
||||||
r"?)?)?)?)?Z",
|
r"?)?)?)?)?Z",
|
||||||
getattr(re, "ASCII", 0), # py3-only
|
re.ASCII
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -135,7 +121,7 @@ class Data:
|
|||||||
return "%s(%s)" % (self.__class__.__name__, repr(self.data))
|
return "%s(%s)" % (self.__class__.__name__, repr(self.data))
|
||||||
|
|
||||||
|
|
||||||
class PlistTarget(object):
|
class PlistTarget:
|
||||||
""" Event handler using the ElementTree Target API that can be
|
""" Event handler using the ElementTree Target API that can be
|
||||||
passed to a XMLParser to produce property list objects from XML.
|
passed to a XMLParser to produce property list objects from XML.
|
||||||
It is based on the CPython plistlib module's _PlistParser class,
|
It is based on the CPython plistlib module's _PlistParser class,
|
||||||
@ -164,6 +150,12 @@ class PlistTarget(object):
|
|||||||
if use_builtin_types is None:
|
if use_builtin_types is None:
|
||||||
self._use_builtin_types = USE_BUILTIN_TYPES
|
self._use_builtin_types = USE_BUILTIN_TYPES
|
||||||
else:
|
else:
|
||||||
|
if use_builtin_types is False:
|
||||||
|
warnings.warn(
|
||||||
|
"Setting use_builtin_types to False is deprecated and will be "
|
||||||
|
"removed soon.",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
self._use_builtin_types = use_builtin_types
|
self._use_builtin_types = use_builtin_types
|
||||||
self._dict_type = dict_type
|
self._dict_type = dict_type
|
||||||
|
|
||||||
@ -322,7 +314,7 @@ def _dict_element(d, ctx):
|
|||||||
items = sorted(items)
|
items = sorted(items)
|
||||||
ctx.indent_level += 1
|
ctx.indent_level += 1
|
||||||
for key, value in items:
|
for key, value in items:
|
||||||
if not isinstance(key, basestring):
|
if not isinstance(key, str):
|
||||||
if ctx.skipkeys:
|
if ctx.skipkeys:
|
||||||
continue
|
continue
|
||||||
raise TypeError("keys must be strings")
|
raise TypeError("keys must be strings")
|
||||||
@ -374,52 +366,21 @@ def _string_or_data_element(raw_bytes, ctx):
|
|||||||
return _string_element(string, ctx)
|
return _string_element(string, ctx)
|
||||||
|
|
||||||
|
|
||||||
# if singledispatch is available, we use a generic '_make_element' function
|
@singledispatch
|
||||||
# and register overloaded implementations that are run based on the type of
|
def _make_element(value, ctx):
|
||||||
# the first argument
|
raise TypeError("unsupported type: %s" % type(value))
|
||||||
|
|
||||||
if singledispatch is not None:
|
_make_element.register(str)(_string_element)
|
||||||
|
_make_element.register(bool)(_bool_element)
|
||||||
@singledispatch
|
_make_element.register(Integral)(_integer_element)
|
||||||
def _make_element(value, ctx):
|
_make_element.register(float)(_real_element)
|
||||||
raise TypeError("unsupported type: %s" % type(value))
|
_make_element.register(Mapping)(_dict_element)
|
||||||
|
_make_element.register(list)(_array_element)
|
||||||
_make_element.register(unicode)(_string_element)
|
_make_element.register(tuple)(_array_element)
|
||||||
_make_element.register(bool)(_bool_element)
|
_make_element.register(datetime)(_date_element)
|
||||||
_make_element.register(Integral)(_integer_element)
|
_make_element.register(bytes)(_string_or_data_element)
|
||||||
_make_element.register(float)(_real_element)
|
_make_element.register(bytearray)(_data_element)
|
||||||
_make_element.register(Mapping)(_dict_element)
|
_make_element.register(Data)(lambda v, ctx: _data_element(v.data, ctx))
|
||||||
_make_element.register(list)(_array_element)
|
|
||||||
_make_element.register(tuple)(_array_element)
|
|
||||||
_make_element.register(datetime)(_date_element)
|
|
||||||
_make_element.register(bytes)(_string_or_data_element)
|
|
||||||
_make_element.register(bytearray)(_data_element)
|
|
||||||
_make_element.register(Data)(lambda v, ctx: _data_element(v.data, ctx))
|
|
||||||
|
|
||||||
else:
|
|
||||||
# otherwise we use a long switch-like if statement
|
|
||||||
|
|
||||||
def _make_element(value, ctx):
|
|
||||||
if isinstance(value, unicode):
|
|
||||||
return _string_element(value, ctx)
|
|
||||||
elif isinstance(value, bool):
|
|
||||||
return _bool_element(value, ctx)
|
|
||||||
elif isinstance(value, Integral):
|
|
||||||
return _integer_element(value, ctx)
|
|
||||||
elif isinstance(value, float):
|
|
||||||
return _real_element(value, ctx)
|
|
||||||
elif isinstance(value, Mapping):
|
|
||||||
return _dict_element(value, ctx)
|
|
||||||
elif isinstance(value, (list, tuple)):
|
|
||||||
return _array_element(value, ctx)
|
|
||||||
elif isinstance(value, datetime):
|
|
||||||
return _date_element(value, ctx)
|
|
||||||
elif isinstance(value, bytes):
|
|
||||||
return _string_or_data_element(value, ctx)
|
|
||||||
elif isinstance(value, bytearray):
|
|
||||||
return _data_element(value, ctx)
|
|
||||||
elif isinstance(value, Data):
|
|
||||||
return _data_element(value.data, ctx)
|
|
||||||
|
|
||||||
|
|
||||||
# Public functions to create element tree from plist-compatible python
|
# Public functions to create element tree from plist-compatible python
|
||||||
|
@ -2,10 +2,7 @@ from fontTools.misc.py23 import *
|
|||||||
from fontTools.misc import eexec
|
from fontTools.misc import eexec
|
||||||
from .psOperators import *
|
from .psOperators import *
|
||||||
import re
|
import re
|
||||||
try:
|
from collections.abc import Callable
|
||||||
from collections.abc import Callable
|
|
||||||
except ImportError: # python < 3.3
|
|
||||||
from collections import Callable
|
|
||||||
from string import whitespace
|
from string import whitespace
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
"""Helpers for writing unit tests."""
|
"""Helpers for writing unit tests."""
|
||||||
|
|
||||||
try:
|
from collections.abc import Iterable
|
||||||
from collections.abc import Iterable
|
|
||||||
except ImportError: # python < 3.3
|
|
||||||
from collections import Iterable
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
@ -2,6 +2,7 @@ from fontTools.misc.py23 import *
|
|||||||
from fontTools.misc import sstruct
|
from fontTools.misc import sstruct
|
||||||
from . import DefaultTable
|
from . import DefaultTable
|
||||||
import array
|
import array
|
||||||
|
from collections.abc import Mapping
|
||||||
|
|
||||||
hdmxHeaderFormat = """
|
hdmxHeaderFormat = """
|
||||||
> # big endian!
|
> # big endian!
|
||||||
@ -10,11 +11,6 @@ hdmxHeaderFormat = """
|
|||||||
recordSize: l
|
recordSize: l
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
|
||||||
from collections.abc import Mapping
|
|
||||||
except:
|
|
||||||
from UserDict import DictMixin as Mapping
|
|
||||||
|
|
||||||
class _GlyphnamedList(Mapping):
|
class _GlyphnamedList(Mapping):
|
||||||
|
|
||||||
def __init__(self, reverseGlyphOrder, data):
|
def __init__(self, reverseGlyphOrder, data):
|
||||||
|
@ -10,10 +10,7 @@ from fontTools.misc.textTools import safeEval
|
|||||||
from fontTools.ttLib import TTLibError
|
from fontTools.ttLib import TTLibError
|
||||||
from . import DefaultTable
|
from . import DefaultTable
|
||||||
import struct
|
import struct
|
||||||
try:
|
from collections.abc import MutableMapping
|
||||||
from collections.abc import MutableMapping
|
|
||||||
except ImportError:
|
|
||||||
from UserDict import DictMixin as MutableMapping
|
|
||||||
|
|
||||||
|
|
||||||
# Apple's documentation of 'trak':
|
# Apple's documentation of 'trak':
|
||||||
|
@ -5,27 +5,15 @@ import codecs
|
|||||||
import collections
|
import collections
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from numbers import Integral
|
from numbers import Integral
|
||||||
from fontTools.misc.py23 import tounicode, unicode
|
from fontTools.misc.py23 import tounicode
|
||||||
from fontTools.misc import etree
|
from fontTools.misc import etree
|
||||||
from fontTools.misc import plistlib
|
from fontTools.misc import plistlib
|
||||||
from fontTools.ufoLib.plistlib import (
|
from fontTools.ufoLib.plistlib import (
|
||||||
readPlist, readPlistFromString, writePlist, writePlistToString,
|
readPlist, readPlistFromString, writePlist, writePlistToString,
|
||||||
)
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
from collections.abc import Mapping
|
||||||
|
|
||||||
try:
|
|
||||||
from collections.abc import Mapping # python >= 3.3
|
|
||||||
except ImportError:
|
|
||||||
from collections import Mapping
|
|
||||||
|
|
||||||
PY2 = sys.version_info < (3,)
|
|
||||||
if PY2:
|
|
||||||
# This is a ResourceWarning that only happens on py27 at interpreter
|
|
||||||
# finalization, and only when coverage is enabled. We can ignore it.
|
|
||||||
# https://github.com/numpy/numpy/issues/3778#issuecomment-24885336
|
|
||||||
pytestmark = pytest.mark.filterwarnings(
|
|
||||||
"ignore:tp_compare didn't return -1 or -2 for exception"
|
|
||||||
)
|
|
||||||
|
|
||||||
# The testdata is generated using https://github.com/python/cpython/...
|
# The testdata is generated using https://github.com/python/cpython/...
|
||||||
# Mac/Tools/plistlib_generate_testdata.py
|
# Mac/Tools/plistlib_generate_testdata.py
|
||||||
@ -188,7 +176,7 @@ def test_bytes_string(use_builtin_types):
|
|||||||
pl = b"some ASCII bytes"
|
pl = b"some ASCII bytes"
|
||||||
data = plistlib.dumps(pl, use_builtin_types=False)
|
data = plistlib.dumps(pl, use_builtin_types=False)
|
||||||
pl2 = plistlib.loads(data, use_builtin_types=use_builtin_types)
|
pl2 = plistlib.loads(data, use_builtin_types=use_builtin_types)
|
||||||
assert isinstance(pl2, unicode) # it's always a <string>
|
assert isinstance(pl2, str) # it's always a <string>
|
||||||
assert pl2 == pl.decode()
|
assert pl2 == pl.decode()
|
||||||
|
|
||||||
|
|
||||||
@ -516,15 +504,13 @@ def test_writePlistToString(pl_no_builtin_types):
|
|||||||
|
|
||||||
def test_load_use_builtin_types_default():
|
def test_load_use_builtin_types_default():
|
||||||
pl = plistlib.loads(TESTDATA)
|
pl = plistlib.loads(TESTDATA)
|
||||||
expected = plistlib.Data if PY2 else bytes
|
assert isinstance(pl["someData"], bytes)
|
||||||
assert isinstance(pl["someData"], expected)
|
|
||||||
|
|
||||||
|
|
||||||
def test_dump_use_builtin_types_default(pl_no_builtin_types):
|
def test_dump_use_builtin_types_default(pl_no_builtin_types):
|
||||||
data = plistlib.dumps(pl_no_builtin_types)
|
data = plistlib.dumps(pl_no_builtin_types)
|
||||||
pl2 = plistlib.loads(data)
|
pl2 = plistlib.loads(data)
|
||||||
expected = plistlib.Data if PY2 else bytes
|
assert isinstance(pl2["someData"], bytes)
|
||||||
assert isinstance(pl2["someData"], expected)
|
|
||||||
assert pl2 == pl_no_builtin_types
|
assert pl2 == pl_no_builtin_types
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user