Hello community, here is the log from the commit of package python-json_tricks for openSUSE:Leap:15.2 checked in at 2020-04-20 12:55:40 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/python-json_tricks (Old) and /work/SRC/openSUSE:Leap:15.2/.python-json_tricks.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-json_tricks" Mon Apr 20 12:55:40 2020 rev:6 rq:795582 version:3.15.2 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/python-json_tricks/python-json_tricks.changes 2020-04-14 14:21:09.469282402 +0200 +++ /work/SRC/openSUSE:Leap:15.2/.python-json_tricks.new.2738/python-json_tricks.changes 2020-04-20 12:55:52.720765200 +0200 @@ -1,0 +2,7 @@ +Thu Apr 16 11:38:33 UTC 2020 - pgaj...@suse.com + +- version update to 3.15.2 + * Deprecation warning ignore_comments + * Fix the code and tests for python2 + +------------------------------------------------------------------- Old: ---- pyjson_tricks-3.15.0.tar.gz New: ---- pyjson_tricks-3.15.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-json_tricks.spec ++++++ --- /var/tmp/diff_new_pack.oVoiD4/_old 2020-04-20 12:55:53.012765659 +0200 +++ /var/tmp/diff_new_pack.oVoiD4/_new 2020-04-20 12:55:53.016765665 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-json_tricks -Version: 3.15.0 +Version: 3.15.2 Release: 0 Summary: Extra features for Python's JSON License: BSD-3-Clause @@ -61,8 +61,6 @@ %prep %setup -q -n pyjson_tricks-%{version} -# py3 only syntax in this file -rm tests/test_utils.py %build %python_build ++++++ pyjson_tricks-3.15.0.tar.gz -> pyjson_tricks-3.15.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.15.0/README.rst new/pyjson_tricks-3.15.2/README.rst --- old/pyjson_tricks-3.15.0/README.rst 2020-04-05 22:48:35.000000000 +0200 +++ new/pyjson_tricks-3.15.2/README.rst 2020-04-12 23:14:07.000000000 +0200 @@ -227,6 +227,8 @@ Comments +++++++++++++++++++++++++++++++++++++++ +*Warning: in the next major version, comment parsing will be opt-in, not default anymore (for performance reasons). Update your code now to pass `ignore_comments=True` explicitly if you want comment parsing.* + This package uses ``#`` and ``//`` for comments, which seem to be the most common conventions, though only the latter is valid javascript. For example, you could call ``loads`` on the following string:: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.15.0/json_tricks/_version.py new/pyjson_tricks-3.15.2/json_tricks/_version.py --- old/pyjson_tricks-3.15.0/json_tricks/_version.py 2020-04-05 22:48:35.000000000 +0200 +++ new/pyjson_tricks-3.15.2/json_tricks/_version.py 2020-04-12 23:14:07.000000000 +0200 @@ -1,3 +1,3 @@ -VERSION = '3.15.0' +VERSION = '3.15.2' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.15.0/json_tricks/decoders.py new/pyjson_tricks-3.15.2/json_tricks/decoders.py --- old/pyjson_tricks-3.15.0/json_tricks/decoders.py 2020-04-05 22:48:35.000000000 +0200 +++ new/pyjson_tricks-3.15.2/json_tricks/decoders.py 2020-04-12 23:14:07.000000000 +0200 @@ -5,7 +5,7 @@ from fractions import Fraction from json_tricks import NoEnumException, NoPandasException, NoNumpyException -from .utils import ClassInstanceHookBase, nested_index, str_type, gzip_decompress +from .utils import ClassInstanceHookBase, nested_index, str_type, gzip_decompress, filtered_wrapper class DuplicateJsonKeyException(Exception): @@ -17,17 +17,18 @@ Hook that converts json maps to the appropriate python type (dict or OrderedDict) and then runs any number of hooks on the individual maps. """ - def __init__(self, ordered=True, obj_pairs_hooks=None, allow_duplicates=True): + def __init__(self, ordered=True, obj_pairs_hooks=None, allow_duplicates=True, properties=None): """ :param ordered: True if maps should retain their ordering. :param obj_pairs_hooks: An iterable of hooks to apply to elements. """ + self.properties = properties or {} self.map_type = OrderedDict if not ordered: self.map_type = dict self.obj_pairs_hooks = [] if obj_pairs_hooks: - self.obj_pairs_hooks = list(obj_pairs_hooks) + self.obj_pairs_hooks = list(filtered_wrapper(hook) for hook in obj_pairs_hooks) self.allow_duplicates = allow_duplicates def __call__(self, pairs): @@ -35,12 +36,12 @@ known = set() for key, value in pairs: if key in known: - raise DuplicateJsonKeyException(('Trying to load a json map which contains a' + - ' duplicate key "{0:}" (but allow_duplicates is False)').format(key)) + raise DuplicateJsonKeyException(('Trying to load a json map which contains a ' + + 'duplicate key "{0:}" (but allow_duplicates is False)').format(key)) known.add(key) map = self.map_type(pairs) for hook in self.obj_pairs_hooks: - map = hook(map) + map = hook(map, properties=self.properties) return map @@ -139,13 +140,14 @@ This hook tries to convert json encoded by enum_instance_encode back to it's original instance. It only works if the environment is the same, e.g. the enum is similarly importable and hasn't changed. """ - def __call__(self, dct): + def __call__(self, dct, properties=None): if not isinstance(dct, dict): return dct if '__enum__' not in dct: return dct + cls_lookup_map = properties.get('cls_lookup_map', {}) mod, name = dct['__enum__']['__enum_instance_type__'] - Cls = self.get_cls_from_instance_type(mod, name) + Cls = self.get_cls_from_instance_type(mod, name, cls_lookup_map=cls_lookup_map) return Cls[dct['__enum__']['name']] @@ -154,13 +156,14 @@ This hook tries to convert json encoded by class_instance_encoder back to it's original instance. It only works if the environment is the same, e.g. the class is similarly importable and hasn't changed. """ - def __call__(self, dct): + def __call__(self, dct, properties=None): if not isinstance(dct, dict): return dct if '__instance_type__' not in dct: return dct + cls_lookup_map = properties.get('cls_lookup_map', {}) or {} mod, name = dct['__instance_type__'] - Cls = self.get_cls_from_instance_type(mod, name) + Cls = self.get_cls_from_instance_type(mod, name, cls_lookup_map=cls_lookup_map) try: obj = Cls.__new__(Cls) except TypeError: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.15.0/json_tricks/encoders.py new/pyjson_tricks-3.15.2/json_tricks/encoders.py --- old/pyjson_tricks-3.15.0/json_tricks/encoders.py 2020-04-05 22:48:35.000000000 +0200 +++ new/pyjson_tricks-3.15.2/json_tricks/encoders.py 2020-04-12 23:14:07.000000000 +0200 @@ -1,14 +1,13 @@ import warnings from datetime import datetime, date, time, timedelta +from decimal import Decimal from fractions import Fraction from functools import wraps from json import JSONEncoder from sys import version, stderr -from decimal import Decimal -from .utils import hashodict, get_arg_names, \ - get_module_name_from_object, NoEnumException, NoPandasException, \ - NoNumpyException, str_type, JsonTricksDeprecation, gzip_compress +from .utils import hashodict, get_module_name_from_object, NoEnumException, NoPandasException, \ + NoNumpyException, str_type, JsonTricksDeprecation, gzip_compress, filtered_wrapper def _fallback_wrapper(encoder): @@ -35,21 +34,6 @@ return fallback_value -def filtered_wrapper(encoder): - """ - Filter kwargs passed to encoder. - """ - if hasattr(encoder, "default"): - encoder = encoder.default - elif not hasattr(encoder, '__call__'): - raise TypeError('`obj_encoder` {0:} does not have `default` method and is not callable'.format(enc)) - names = get_arg_names(encoder) - - def wrapper(*args, **kwargs): - return encoder(*args, **{k: v for k, v in kwargs.items() if k in names}) - return wrapper - - class TricksEncoder(JSONEncoder): """ Encoder that runs any number of encoder functions or instances on @@ -389,7 +373,6 @@ """ From ndarray to base64 encoded, gzipped binary data. """ - import gzip from base64 import standard_b64encode assert array.flags['C_CONTIGUOUS'], 'only C memory order is (currently) supported for compact ndarray format' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.15.0/json_tricks/nonp.py new/pyjson_tricks-3.15.2/json_tricks/nonp.py --- old/pyjson_tricks-3.15.0/json_tricks/nonp.py 2020-04-05 22:48:35.000000000 +0200 +++ new/pyjson_tricks-3.15.2/json_tricks/nonp.py 2020-04-12 23:14:07.000000000 +0200 @@ -1,9 +1,9 @@ - +import warnings from json import loads as json_loads from os import fsync from sys import exc_info -from json_tricks.utils import is_py3, dict_default, gzip_compress, gzip_decompress +from json_tricks.utils import is_py3, dict_default, gzip_compress, gzip_decompress, JsonTricksDeprecation from .utils import str_type, NoNumpyException # keep 'unused' imports from .comment import strip_comments # keep 'unused' imports #TODO @mark: imports removed? @@ -176,8 +176,9 @@ return txt -def loads(string, preserve_order=True, ignore_comments=True, decompression=None, obj_pairs_hooks=DEFAULT_HOOKS, - extra_obj_pairs_hooks=(), cls_lookup_map=None, allow_duplicates=True, conv_str_byte=False, **jsonkwargs): +def loads(string, preserve_order=True, ignore_comments=None, decompression=None, obj_pairs_hooks=DEFAULT_HOOKS, + extra_obj_pairs_hooks=(), cls_lookup_map=None, allow_duplicates=True, conv_str_byte=False, + properties=None, **jsonkwargs): """ Convert a nested data structure to a json string. @@ -212,18 +213,30 @@ 'for example bytevar.encode("utf-8") if utf-8 is the encoding. Alternatively you can ' 'force an attempt by passing conv_str_byte=True, but this may cause decoding issues.') .format(type(string))) - if ignore_comments: - string = strip_comments(string) - obj_pairs_hooks = tuple(obj_pairs_hooks) - _cih_instance.cls_lookup_map = cls_lookup_map or {} - _eih_instance.cls_lookup_map = cls_lookup_map or {} - hooks = tuple(extra_obj_pairs_hooks) + obj_pairs_hooks - hook = TricksPairHook(ordered=preserve_order, obj_pairs_hooks=hooks, allow_duplicates=allow_duplicates) + if ignore_comments or ignore_comments is None: + new_string = strip_comments(string) + if ignore_comments is None and not getattr(loads, '_ignore_comments_warned', False) and string != new_string: + warnings.warn('`json_tricks.load(s)` stripped some comments, but `ignore_comments` was ' + 'not passed; in the next major release, the behaviour when `ignore_comments` is not ' + 'passed will change; it is recommended to explicitly pass `ignore_comments=True` if ' + 'you want to strip comments; see https://github.com/mverleg/pyjson_tricks/issues/74', + JsonTricksDeprecation) + loads._ignore_comments_warned = True + string = new_string + properties = properties or {} + dict_default(properties, 'preserve_order', preserve_order) + dict_default(properties, 'ignore_comments', ignore_comments) + dict_default(properties, 'decompression', decompression) + dict_default(properties, 'cls_lookup_map', cls_lookup_map) + dict_default(properties, 'allow_duplicates', allow_duplicates) + hooks = tuple(extra_obj_pairs_hooks) + tuple(obj_pairs_hooks) + hook = TricksPairHook(ordered=preserve_order, obj_pairs_hooks=hooks, allow_duplicates=allow_duplicates, properties=properties) return json_loads(string, object_pairs_hook=hook, **jsonkwargs) -def load(fp, preserve_order=True, ignore_comments=True, decompression=None, obj_pairs_hooks=DEFAULT_HOOKS, - extra_obj_pairs_hooks=(), cls_lookup_map=None, allow_duplicates=True, conv_str_byte=False, **jsonkwargs): +def load(fp, preserve_order=True, ignore_comments=None, decompression=None, obj_pairs_hooks=DEFAULT_HOOKS, + extra_obj_pairs_hooks=(), cls_lookup_map=None, allow_duplicates=True, conv_str_byte=False, + properties=None, **jsonkwargs): """ Convert a nested data structure to a json string. @@ -250,6 +263,6 @@ 'opened in binary mode; be sure to set file mode to something like "rb".').with_traceback(exc_info()[2]) return loads(string, preserve_order=preserve_order, ignore_comments=ignore_comments, decompression=decompression, obj_pairs_hooks=obj_pairs_hooks, extra_obj_pairs_hooks=extra_obj_pairs_hooks, cls_lookup_map=cls_lookup_map, - allow_duplicates=allow_duplicates, conv_str_byte=conv_str_byte, **jsonkwargs) + allow_duplicates=allow_duplicates, conv_str_byte=conv_str_byte, properties=properties, **jsonkwargs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.15.0/json_tricks/utils.py new/pyjson_tricks-3.15.2/json_tricks/utils.py --- old/pyjson_tricks-3.15.0/json_tricks/utils.py 2020-04-05 22:48:35.000000000 +0200 +++ new/pyjson_tricks-3.15.2/json_tricks/utils.py 2020-04-12 23:14:07.000000000 +0200 @@ -28,7 +28,7 @@ try: from inspect import getfullargspec except ImportError: - from inspect import getargspec + from inspect import getargspec, isfunction def get_arg_names(callable): if type(callable) == partial and version_info[0] == 2: if not hasattr(get_arg_names, '__warned_partial_argspec'): @@ -36,7 +36,10 @@ warnings.warn("'functools.partial' and 'inspect.getargspec' are not compatible in this Python version; " "ignoring the 'partial' wrapper when inspecting arguments of {}, which can lead to problems".format(callable)) return set(getargspec(callable.func).args) - argspec = getargspec(callable) + if isfunction(callable): + argspec = getargspec(callable) + else: + argspec = getargspec(callable.__call__) return set(argspec.args) else: #todo: this is not covered in test case (py 3+ uses `signature`, py2 `getfullargspec`); consider removing it @@ -49,6 +52,21 @@ return set(sig.parameters.keys()) +def filtered_wrapper(encoder): + """ + Filter kwargs passed to encoder. + """ + if hasattr(encoder, "default"): + encoder = encoder.default + elif not hasattr(encoder, '__call__'): + raise TypeError('`obj_encoder` {0:} does not have `default` method and is not callable'.format(enc)) + names = get_arg_names(encoder) + + def wrapper(*args, **kwargs): + return encoder(*args, **{k: v for k, v in kwargs.items() if k in names}) + return wrapper + + class NoNumpyException(Exception): """ Trying to use numpy features, but numpy cannot be found. """ @@ -66,19 +84,17 @@ class ClassInstanceHookBase(object): - def __init__(self, cls_lookup_map=None): - self.cls_lookup_map = cls_lookup_map or {} - - def get_cls_from_instance_type(self, mod, name): + def get_cls_from_instance_type(self, mod, name, cls_lookup_map): + Cls = ValueError() if mod is None: try: Cls = getattr((__import__('__main__')), name) - except (ImportError, AttributeError) as err: - if name not in self.cls_lookup_map: + except (ImportError, AttributeError): + if name not in cls_lookup_map: raise ImportError(('class {0:s} seems to have been exported from the main file, which means ' - 'it has no module/import path set; you need to provide cls_lookup_map which maps names ' - 'to classes').format(name)) - Cls = self.cls_lookup_map[name] + 'it has no module/import path set; you need to provide loads argument' + '`cls_lookup_map={{"{0}": Class}}` to locate the class').format(name)) + Cls = cls_lookup_map[name] else: imp_err = None try: @@ -87,16 +103,15 @@ imp_err = ('encountered import error "{0:}" while importing "{1:}" to decode a json file; perhaps ' 'it was encoded in a different environment where {1:}.{2:} was available').format(err, mod, name) else: - if not hasattr(module, name): - imp_err = 'imported "{0:}" but could find "{1:}" inside while decoding a json file (found {2:}'.format( + if hasattr(module, name): + Cls = getattr(module, name) + else: + imp_err = 'imported "{0:}" but could find "{1:}" inside while decoding a json file (found {2:})'.format( module, name, ', '.join(attr for attr in dir(module) if not attr.startswith('_'))) - Cls = getattr(module, name) if imp_err: - if 'name' in self.cls_lookup_map: - Cls = self.cls_lookup_map[name] - else: - raise ImportError(imp_err) - + Cls = cls_lookup_map.get(name, None) + if Cls is None: + raise ImportError('{}; add the class to `cls_lookup_map={{"{}": Class}}` argument'.format(imp_err, name)) return Cls diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.15.0/tests/test_bare.py new/pyjson_tricks-3.15.2/tests/test_bare.py --- old/pyjson_tricks-3.15.0/tests/test_bare.py 2020-04-05 22:48:35.000000000 +0200 +++ new/pyjson_tricks-3.15.2/tests/test_bare.py 2020-04-12 23:14:07.000000000 +0200 @@ -12,12 +12,13 @@ from os.path import join from tempfile import mkdtemp +from _pytest.recwarn import warns from pytest import raises, fail from json_tricks import fallback_ignore_unknown, DuplicateJsonKeyException from json_tricks.nonp import strip_comments, dump, dumps, load, loads, \ ENCODING -from json_tricks.utils import is_py3, gzip_compress +from json_tricks.utils import is_py3, gzip_compress, JsonTricksDeprecation from .test_class import MyTestCls, CustomEncodeCls, SubClass, SuperClass, SlotsBase, SlotsDictABC, SlotsStr, \ SlotsABCDict, SlotsABC @@ -143,6 +144,12 @@ } """ +test_object_for_comment_strings = { + "hello": "Wor#d", "Bye": "\"M#rk\"", "yes\\\"": 5, + "quote": "\"th#t's\" what she said", + "list": [1, 1, "#", "\"", "\\", 8], "dict": {"q": 7} +} + test_json_duplicates = """{"test": 42, "test": 37}""" @@ -153,6 +160,45 @@ assert valid == test_json_without_comments.replace('#', '//') +def test_ignore_comments_deprecation(): + # https://github.com/mverleg/pyjson_tricks/issues/74 + + # First time should have deprecation warning + loads._ignore_comments_warned_ = False + with warns(JsonTricksDeprecation): + loads(test_json_with_comments) + + # Second time there should be no warning + # noinspection PyTypeChecker + with warns(None) as captured: + loaded = loads(test_json_with_comments) + assert len(captured) == 0 + assert loaded == test_object_for_comment_strings + + # Passing a string without comments should not have a warning + loads._ignore_comments_warned_ = False + # noinspection PyTypeChecker + with warns(None) as captured: + loaded = loads(test_json_without_comments) + assert len(captured) == 0 + + # Passing True for argument explicitly should not have a warning + loads._ignore_comments_warned_ = False + # noinspection PyTypeChecker + with warns(None) as captured: + loaded = loads(test_json_with_comments, ignore_comments=True) + assert len(captured) == 0 + assert loaded == test_object_for_comment_strings + + # Passing False for argument explicitly should not have a warning + loads._ignore_comments_warned_ = False + # noinspection PyTypeChecker + with warns(None) as captured: + loaded = loads(test_json_without_comments, ignore_comments=False) + assert len(captured) == 0 + assert loaded == test_object_for_comment_strings + + ordered_map = OrderedDict(( ('elephant', None), ('chicken', None), @@ -280,6 +326,38 @@ SuperClass.cls_attr = 37 +def test_cls_lookup_map_fail(): + class LocalCls(object): + def __init__(self, val): + self.value = val + original = [LocalCls(37), LocalCls(42)] + txt = dumps(original) + with raises(ImportError) as err: + loads(txt) + assert 'LocalCls' in str(err.value) + assert 'cls_lookup_map' in str(err.value) + with raises(ImportError) as err: + loads(txt, cls_lookup_map=globals()) + assert 'LocalCls' in str(err.value) + assert 'cls_lookup_map' in str(err.value) + + +def test_cls_lookup_map_success(): + class LocalCls(object): + def __init__(self, val): + self.value = val + original = [LocalCls(37), LocalCls(42)] + txt = dumps(original) + back = loads(txt, cls_lookup_map=dict(LocalCls=LocalCls)) + assert len(original) == len(back) == 2 + assert original[0].value == back[0].value + assert original[1].value == back[1].value + back = loads(txt, properties=dict(cls_lookup_map=dict(LocalCls=LocalCls))) + assert len(original) == len(back) == 2 + assert original[0].value == back[0].value + assert original[1].value == back[1].value + + def test_cls_slots(): slots = [SlotsBase(), SlotsDictABC(), SlotsStr(), SlotsABCDict(), SlotsABC()] txt = dumps(slots) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.15.0/tests/test_np.py new/pyjson_tricks-3.15.2/tests/test_np.py --- old/pyjson_tricks-3.15.0/tests/test_np.py 2020-04-05 22:48:35.000000000 +0200 +++ new/pyjson_tricks-3.15.2/tests/test_np.py 2020-04-12 23:14:07.000000000 +0200 @@ -11,7 +11,6 @@ float16, float32, float64, complex64, complex128, zeros, ndindex from numpy.core.umath import exp from numpy.testing import assert_equal -from pytest import raises from json_tricks import numpy_encode from json_tricks.np import dump, dumps, load, loads diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.15.0/tests/test_utils.py new/pyjson_tricks-3.15.2/tests/test_utils.py --- old/pyjson_tricks-3.15.0/tests/test_utils.py 2020-04-05 22:48:35.000000000 +0200 +++ new/pyjson_tricks-3.15.2/tests/test_utils.py 2020-04-12 23:14:07.000000000 +0200 @@ -36,7 +36,7 @@ def base85_vsbase64_performance(): from base64 import b85encode, standard_b64encode, urlsafe_b64encode from random import getrandbits - test_data = bytearray(getrandbits(8) for _ in range(10_000_000)) + test_data = bytearray(getrandbits(8) for _ in range(10000000)) from timeit import default_timer print('')