Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-json_tricks for openSUSE:Factory checked in at 2023-12-08 22:32:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-json_tricks (Old) and /work/SRC/openSUSE:Factory/.python-json_tricks.new.25432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-json_tricks" Fri Dec 8 22:32:01 2023 rev:13 rq:1131723 version:3.17.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-json_tricks/python-json_tricks.changes 2023-06-18 23:08:31.801671684 +0200 +++ /work/SRC/openSUSE:Factory/.python-json_tricks.new.25432/python-json_tricks.changes 2023-12-08 22:32:20.452634468 +0100 @@ -1,0 +2,7 @@ +Thu Dec 7 22:24:21 UTC 2023 - Dirk Müller <[email protected]> + +- update to 3.17.3: + * drop python 2 support + * datetime fixes + +------------------------------------------------------------------- Old: ---- pyjson_tricks-3.17.1.tar.gz New: ---- pyjson_tricks-3.17.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-json_tricks.spec ++++++ --- /var/tmp/diff_new_pack.Dnekft/_old 2023-12-08 22:32:21.068657135 +0100 +++ /var/tmp/diff_new_pack.Dnekft/_new 2023-12-08 22:32:21.072657282 +0100 @@ -16,9 +16,8 @@ # -%bcond_without python2 Name: python-json_tricks -Version: 3.17.1 +Version: 3.17.3 Release: 0 Summary: Extra features for Python's JSON License: BSD-3-Clause @@ -37,15 +36,7 @@ BuildRequires: %{python_module pandas} BuildRequires: %{python_module pytest} BuildRequires: %{python_module pytz} -%if %{with python2} -BuildRequires: python-enum34 -BuildRequires: python-pathlib -%endif # /SECTION -%ifpython2 -Requires: python-pathlib -Recommends: python-enum34 -%endif %python_subpackages %description ++++++ pyjson_tricks-3.17.1.tar.gz -> pyjson_tricks-3.17.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.17.1/.github/workflows/tests.yml new/pyjson_tricks-3.17.3/.github/workflows/tests.yml --- old/pyjson_tricks-3.17.1/.github/workflows/tests.yml 2023-06-17 13:53:18.000000000 +0200 +++ new/pyjson_tricks-3.17.3/.github/workflows/tests.yml 2023-08-19 14:03:15.000000000 +0200 @@ -21,7 +21,6 @@ 'all' ] python-version: [ - '2.7', '3.7', '3.8', '3.9', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.17.1/README.md new/pyjson_tricks-3.17.3/README.md --- old/pyjson_tricks-3.17.1/README.md 2023-06-17 13:53:18.000000000 +0200 +++ new/pyjson_tricks-3.17.3/README.md 2023-08-19 14:03:15.000000000 +0200 @@ -20,7 +20,7 @@ Several keys of the format `__keyname__` have special meanings, and more might be added in future releases. -If you\'re considering JSON-but-with-comments as a config file format, +If you're considering JSON-but-with-comments as a config file format, have a look at [HJSON](https://github.com/hjson/hjson-py), it might be more appropriate. For other purposes, keep reading! @@ -78,19 +78,20 @@ `json_tricks.loads`. Note that the memory order (`Corder`) is only stored in v3.1 and later and for arrays with at least 2 dimensions. -As you see, this uses the magic key `__ndarray__`. Don\'t use -`__ndarray__` as a dictionary key unless you\'re trying to make a numpy -array (and know what you\'re doing). +As you see, this uses the magic key `__ndarray__`. Don't use +`__ndarray__` as a dictionary key unless you're trying to make a numpy +array (and know what you're doing). Numpy scalars are also serialized (v3.5+). They are represented by the closest python primitive type. A special representation was not -feasible, because Python\'s json implementation serializes some numpy +feasible, because Python's json implementation serializes some numpy types as primitives, without consulting custom encoders. If you want to preserve the exact numpy type, use [encode_scalars_inplace](https://json-tricks.readthedocs.io/en/latest/#json_tricks.np_utils.encode_scalars_inplace). -There is also a compressed format. From the next major release, this -will be default when using compression. For now you can use it as: +There is also a compressed format (thanks `claydugo` for fix). From +the next major release, this will be default when using compression. +For now, you can use it as: ``` python dumps(data, compression=True, properties={'ndarray_compact': True}) @@ -124,7 +125,7 @@ `json_tricks` can serialize class instances. If the class behaves normally (not generated dynamic, no `__new__` or -`__metaclass__` magic, etc) *and* all it\'s attributes are serializable, +`__metaclass__` magic, etc) *and* all it's attributes are serializable, then this should work by default. ``` python @@ -140,7 +141,7 @@ cls_instance_again = loads(json) ``` -You\'ll get your instance back. Here the json looks like this: +You'll get your instance back. Here the json looks like this: ``` javascript { @@ -159,12 +160,12 @@ As you can see, this stores the module and class name. The class must be importable from the same module when decoding (and should not have -changed). If it isn\'t, you have to manually provide a dictionary to +changed). If it isn't, you have to manually provide a dictionary to `cls_lookup_map` when loading in which the class name can be looked up. Note that if the class is imported, then `globals()` is such a dictionary (so try `loads(json, cls_lookup_map=glboals())`). Also note -that if the class is defined in the \'top\' script (that you\'re calling -directly), then this isn\'t a module and the import part cannot be +that if the class is defined in the 'top' script (that you're calling +directly), then this isn't a module and the import part cannot be extracted. Only the class name will be stored; it can then only be deserialized in the same script, or if you provide `cls_lookup_map`. @@ -180,7 +181,7 @@ } ``` -If the instance doesn\'t serialize automatically, or if you want custom +If the instance doesn't serialize automatically, or if you want custom behaviour, then you can implement `__json__encode__(self)` and `__json_decode__(self, **attributes)` methods, like so: @@ -200,18 +201,18 @@ self.irrelevant = 12 ``` -As you\'ve seen, this uses the magic key `__instance_type__`. Don\'t use -`__instance_type__` as a dictionary key unless you know what you\'re +As you've seen, this uses the magic key `__instance_type__`. Don't use +`__instance_type__` as a dictionary key unless you know what you're doing. ## Date, time, datetime and timedelta Date, time, datetime and timedelta objects are stored as dictionaries of -\"day\", \"hour\", \"millisecond\" etc keys, for each nonzero property. +"day", "hour", "millisecond" etc keys, for each nonzero property. -Timezone name is also stored in case it is set. You\'ll need to have -`pytz` installed to use timezone-aware date/times, it\'s not needed for -naive date/times. +Timezone name is also stored in case it is set, as is DST (thanks `eumir`). +You'll need to have `pytz` installed to use timezone-aware date/times, +it's not needed for naive date/times. ``` javascript { @@ -230,11 +231,11 @@ This approach was chosen over timestamps for readability and consistency between date and time, and over a single string to prevent parsing problems and reduce dependencies. Note that if `primitives=True`, -date/times are encoded as ISO 8601, but they won\'t be restored +date/times are encoded as ISO 8601, but they won't be restored automatically. -Don\'t use `__date__`, `__time__`, `__datetime__`, `__timedelta__` or -`__tzinfo__` as dictionary keys unless you know what you\'re doing, as +Don't use `__date__`, `__time__`, `__datetime__`, `__timedelta__` or +`__tzinfo__` as dictionary keys unless you know what you're doing, as they have special meaning. ## Order @@ -258,7 +259,7 @@ ``` where `preserve_order=True` is added for emphasis; it can be left out -since it\'s the default. +since it's the default. As a note on [performance](http://stackoverflow.com/a/8177061/723090), both dicts and OrderedDicts have the same scaling for getting and @@ -282,9 +283,9 @@ For example, you could call `loads` on the following string: { # "comment 1 - "hello": "Wor#d", "Bye": "\"M#rk\"", "yes\\\"": 5,# comment" 2 - "quote": "\"th#t's\" what she said", // comment "3" - "list": [1, 1, "#", "\"", "\\", 8], "dict": {"q": 7} #" comment 4 with quotes + "hello": "Wor#d", "Bye": ""M#rk"", "yes\\"": 5,# comment" 2 + "quote": ""th#t's" what she said", // comment "3" + "list": [1, 1, "#", """, "\", 8], "dict": {"q": 7} #" comment 4 with quotes } // comment 5 @@ -292,9 +293,9 @@ ``` javascript { - "hello": "Wor#d", "Bye": "\"M#rk\"", "yes\\\"": 5, - "quote": "\"th#t's\" what she said", - "list": [1, 1, "#", "\"", "\\", 8], "dict": {"q": 7} + "hello": "Wor#d", "Bye": ""M#rk"", "yes\\"": 5, + "quote": ""th#t's" what she said", + "list": [1, 1, "#", """, "\", 8], "dict": {"q": 7} } ``` @@ -353,9 +354,9 @@ You can also choose to store things as their closest primitive type (e.g. arrays and sets as lists, decimals as floats). This may be -desirable if you don\'t care about the exact type, or you are loading -the json in another language (which doesn\'t restore python types). -It\'s also smaller. +desirable if you don't care about the exact type, or you are loading +the json in another language (which doesn't restore python types). +It's also smaller. To forego meta data and store primitives instead, pass `primitives` to `dump(s)`. This is available in version `3.8` and later. Example: @@ -457,18 +458,17 @@ Note that valid json is produced either way: ``json-tricks`` stores meta data as normal json, but other packages probably won't interpret it. Note that valid json is produced either way: `json-tricks` stores meta -data as normal json, but other packages probably won\'t interpret it. +data as normal json, but other packages probably won't interpret it. # Usage & contributions -Code is under [Revised BSD -License](https://github.com/mverleg/pyjson_tricks/blob/master/LICENSE.txt) +Code is under [Revised BSD License](LICENSE.txt) so you can use it for most purposes including commercially. Contributions are very welcome! Bug reports, feature suggestions and code contributions help this project become more useful for everyone! There is a short [contribution -guide](https://github.com/mverleg/pyjson_tricks/blob/master/CONTRIBUTING.txt). +guide](CONTRIBUTING.md). Contributors not yet mentioned: `janLo` (performance boost). @@ -479,5 +479,4 @@  -To run the tests manually for your version, see [this -guide](https://github.com/mverleg/pyjson_tricks/blob/master/tests/run_locally.rst). \ No newline at end of file +To run the tests manually for your version, see [this guide](tests/run_locally.md). \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.17.1/json_tricks/_version.py new/pyjson_tricks-3.17.3/json_tricks/_version.py --- old/pyjson_tricks-3.17.1/json_tricks/_version.py 2023-06-17 13:53:18.000000000 +0200 +++ new/pyjson_tricks-3.17.3/json_tricks/_version.py 2023-08-19 14:03:15.000000000 +0200 @@ -1,3 +1,3 @@ -VERSION = '3.17.1' +VERSION = '3.17.3' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.17.1/json_tricks/decoders.py new/pyjson_tricks-3.17.3/json_tricks/decoders.py --- old/pyjson_tricks-3.17.1/json_tricks/decoders.py 2023-06-17 13:53:18.000000000 +0200 +++ new/pyjson_tricks-3.17.3/json_tricks/decoders.py 2023-08-19 14:03:15.000000000 +0200 @@ -80,7 +80,7 @@ microsecond=dct.get('microsecond', 0)) if tzinfo is None: return dt - return tzinfo.localize(dt) + return tzinfo.localize(dt, is_dst=dct.get('is_dst', None)) elif '__timedelta__' in dct: return timedelta(days=dct.get('days', 0), seconds=dct.get('seconds', 0), microseconds=dct.get('microseconds', 0)) @@ -325,7 +325,7 @@ np_type = np_type.newbyteorder('>') elif data_endianness != 'native': warnings.warn('array of shape {} has unknown endianness \'{}\''.format(shape, data_endianness)) - data = frombuffer(data, dtype=np_type) + data = frombuffer(bytearray(data), dtype=np_type) return data.reshape(shape) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.17.1/json_tricks/encoders.py new/pyjson_tricks-3.17.3/json_tricks/encoders.py --- old/pyjson_tricks-3.17.1/json_tricks/encoders.py 2023-06-17 13:53:18.000000000 +0200 +++ new/pyjson_tricks-3.17.3/json_tricks/encoders.py 2023-08-19 14:03:15.000000000 +0200 @@ -96,14 +96,21 @@ ('day', obj.day), ('hour', obj.hour), ('minute', obj.minute), ('second', obj.second), ('microsecond', obj.microsecond)]) if obj.tzinfo: - dct['tzinfo'] = obj.tzinfo.zone + if hasattr(obj.tzinfo, 'zone'): + dct['tzinfo'] = obj.tzinfo.zone + else: + dct['tzinfo'] = obj.tzinfo.tzname(None) + dct['is_dst'] = bool(obj.dst()) elif isinstance(obj, date): dct = hashodict([('__date__', None), ('year', obj.year), ('month', obj.month), ('day', obj.day)]) elif isinstance(obj, time): dct = hashodict([('__time__', None), ('hour', obj.hour), ('minute', obj.minute), ('second', obj.second), ('microsecond', obj.microsecond)]) if obj.tzinfo: - dct['tzinfo'] = obj.tzinfo.zone + if hasattr(obj.tzinfo, 'zone'): + dct['tzinfo'] = obj.tzinfo.zone + else: + dct['tzinfo'] = obj.tzinfo.tzname(None) elif isinstance(obj, timedelta): if primitives: return obj.total_seconds() @@ -113,7 +120,7 @@ else: return obj for key, val in tuple(dct.items()): - if not key.startswith('__') and not val: + if not key.startswith('__') and not key == 'is_dst' and not val: del dct[key] return dct diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.17.1/tests/test_np.py new/pyjson_tricks-3.17.3/tests/test_np.py --- old/pyjson_tricks-3.17.1/tests/test_np.py 2023-06-17 13:53:18.000000000 +0200 +++ new/pyjson_tricks-3.17.3/tests/test_np.py 2023-08-19 14:03:15.000000000 +0200 @@ -366,3 +366,18 @@ json = dumps(data) assert_equal(loads(json), data, 'shape = {} ; json = {}'.format(data.shape, json)) +def test_decode_writeable(): + # issue https://github.com/mverleg/pyjson_tricks/issues/90 + data = zeros((2, 2)) + + data_uncompressed = dumps(data) + data_compressed = dumps(data, properties={'ndarray_compact': True}) + + reloaded_uncompressed = loads(data_uncompressed) + reloaded_compressed = loads(data_compressed) + + assert array_equal(data, reloaded_uncompressed) + assert array_equal(data, reloaded_compressed) + + assert reloaded_uncompressed.flags.writeable + assert reloaded_compressed.flags.writeable diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyjson_tricks-3.17.1/tests/test_tz.py new/pyjson_tricks-3.17.3/tests/test_tz.py --- old/pyjson_tricks-3.17.1/tests/test_tz.py 2023-06-17 13:53:18.000000000 +0200 +++ new/pyjson_tricks-3.17.3/tests/test_tz.py 2023-08-19 14:03:15.000000000 +0200 @@ -6,62 +6,101 @@ work with just Python code functionality, and are tested in `nonp`. """ -from datetime import datetime, date, time, timedelta +from datetime import datetime, date, time, timedelta, timezone from json_tricks import dumps, loads from json_tricks.utils import is_py3 import pytz DTOBJ = [ - datetime(year=1988, month=3, day=15, hour=8, minute=3, second=59, microsecond=7), - pytz.UTC.localize(datetime(year=1988, month=3, day=15, minute=3, second=59, microsecond=7)), - pytz.timezone('Europe/Amsterdam').localize(datetime(year=1988, month=3, day=15, microsecond=7)), - date(year=1988, month=3, day=15), - time(hour=8, minute=3, second=59, microsecond=123), - time(hour=8, second=59, microsecond=123, tzinfo=pytz.timezone('Europe/Amsterdam')), - timedelta(days=2, seconds=3599), - timedelta(days=0, seconds=-42, microseconds=123), - [{'obj': [pytz.timezone('Europe/Amsterdam').localize(datetime(year=1988, month=3, day=15, microsecond=7))]}], + datetime(year=1988, month=3, day=15, hour=8, minute=3, second=59, microsecond=7), + datetime.now(timezone.utc), + pytz.UTC.localize(datetime(year=1988, month=3, day=15, minute=3, second=59, microsecond=7)), + pytz.timezone('Europe/Amsterdam').localize(datetime(year=1988, month=3, day=15, microsecond=7)), + date(year=1988, month=3, day=15), + time(hour=8, minute=3, second=59, microsecond=123), + time(hour=8, second=59, microsecond=123, tzinfo=pytz.timezone('Europe/Amsterdam')), + time(hour=8, second=59, microsecond=123, tzinfo=timezone.utc), + timedelta(days=2, seconds=3599), + timedelta(days=0, seconds=-42, microseconds=123), + [{'obj': [pytz.timezone('Europe/Amsterdam').localize(datetime(year=1988, month=3, day=15, microsecond=7))]}], ] -def test_tzaware_date_time(): - json = dumps(DTOBJ) - back = loads(json) - assert DTOBJ == back - for orig, bck in zip(DTOBJ, back): - assert orig == bck - assert type(orig) == type(bck) - txt = '{"__datetime__": null, "year": 1988, "month": 3, "day": 15, "hour": 8, "minute": 3, ' \ - '"second": 59, "microsecond": 7, "tzinfo": "Europe/Amsterdam"}' - obj = loads(txt) - assert obj == pytz.timezone('Europe/Amsterdam').localize(datetime(year=1988, month=3, day=15, hour=8, minute=3, second=59, microsecond=7)) +def test_tzaware_date_time_without_dst(): + json = dumps(DTOBJ) + back = loads(json) + assert DTOBJ == back + for orig, bck in zip(DTOBJ, back): + assert orig == bck + assert type(orig) == type(bck) + txt = '{"__datetime__": null, "year": 1988, "month": 3, "day": 15, "hour": 8, "minute": 3, ' \ + '"second": 59, "microsecond": 7, "tzinfo": "Europe/Amsterdam"}' + obj = loads(txt) + assert obj == pytz.timezone('Europe/Amsterdam').localize(datetime(year=1988, month=3, day=15, hour=8, minute=3, second=59, microsecond=7)) + + +def test_tzaware_date_time_with_dst(): + json = dumps(DTOBJ) + back = loads(json) + assert DTOBJ == back + for orig, bck in zip(DTOBJ, back): + assert orig == bck + assert type(orig) == type(bck) + txt = '{"__datetime__": null, "year": 1988, "month": 3, "day": 15, "hour": 8, "minute": 3, ' \ + '"second": 59, "microsecond": 7, "tzinfo": "Europe/Amsterdam", "is_dst": true}' + obj = loads(txt) + assert obj == pytz.timezone('Europe/Amsterdam').localize(datetime(year=1988, month=3, day=15, hour=8, minute=3, second=59, microsecond=7)) def test_tzaware_naive_date_time(): - json = dumps(DTOBJ, primitives=True) - back = loads(json) - for orig, bck in zip(DTOBJ, back): - if isinstance(bck, (date, time, datetime,)): - assert isinstance(bck, str if is_py3 else (str, unicode)) - assert bck == orig.isoformat() - elif isinstance(bck, (timedelta,)): - assert isinstance(bck, float) - assert bck == orig.total_seconds() - dt = pytz.timezone('Europe/Amsterdam').localize(datetime(year=1988, month=3, day=15, hour=8, minute=3, second=59, microsecond=7)) - assert dumps(dt, primitives=True).strip('"') == '1988-03-15T08:03:59.000007+01:00' + json = dumps(DTOBJ, primitives=True) + back = loads(json) + for orig, bck in zip(DTOBJ, back): + if isinstance(bck, (date, time, datetime,)): + assert isinstance(bck, str if is_py3 else (str, unicode)) + assert bck == orig.isoformat() + elif isinstance(bck, (timedelta,)): + assert isinstance(bck, float) + assert bck == orig.total_seconds() + dt = pytz.timezone('Europe/Amsterdam').localize(datetime(year=1988, month=3, day=15, hour=8, minute=3, second=59, microsecond=7)) + assert dumps(dt, primitives=True).strip('"') == '1988-03-15T08:03:59.000007+01:00' def test_avoiding_tz_datettime_problem(): - """ - There's a weird problem (bug? feature?) when passing timezone object to datetime constructor. This tests checks that json_tricks doesn't suffer from this problem. - https://github.com/mverleg/pyjson_tricks/issues/41 / https://stackoverflow.com/a/25390097/723090 - """ - tzdt = datetime(2007, 12, 5, 6, 30, 0, 1) - tzdt = pytz.timezone('US/Pacific').localize(tzdt) - back = loads(dumps([tzdt]))[0] - assert pytz.utc.normalize(tzdt) == pytz.utc.normalize(back), \ - "Mismatch due to pytz localizing error {} != {}".format( - pytz.utc.normalize(tzdt), pytz.utc.normalize(back)) - - + """ + There's a weird problem (bug? feature?) when passing timezone object to datetime constructor. This tests checks that json_tricks doesn't suffer from this problem. + https://github.com/mverleg/pyjson_tricks/issues/41 / https://stackoverflow.com/a/25390097/723090 + """ + tzdt = datetime(2007, 12, 5, 6, 30, 0, 1) + tzdt = pytz.timezone('US/Pacific').localize(tzdt) + back = loads(dumps([tzdt]))[0] + assert pytz.utc.normalize(tzdt) == pytz.utc.normalize(back), \ + "Mismatch due to pytz localizing error {} != {}".format( + pytz.utc.normalize(tzdt), pytz.utc.normalize(back)) + + +def test_serialization_remains_unchanged(): + json = dumps(datetime(2023, 10, 29, 1, 30, 0, 0, pytz.UTC) \ + .astimezone(pytz.timezone("Europe/Paris"))) + assert json == '{"__datetime__": null, "year": 2023, "month": 10, "day": 29, ' \ + '"hour": 2, "minute": 30, "tzinfo": "Europe/Paris", "is_dst": false}' + + +def test_before_dst_fold(): + # issue #89 + before_dst = datetime(2023, 10, 29, 0, 30, 0, 0, pytz.UTC) \ + .astimezone(pytz.timezone("Europe/Paris")) + back = loads(dumps(before_dst)) + assert back == before_dst + assert back.tzinfo.zone == before_dst.tzinfo.zone + assert back.utcoffset() == before_dst.utcoffset() + + +def test_after_dst_fold(): + after_dst = datetime(2023, 10, 29, 1, 30, 0, 0, pytz.UTC) \ + .astimezone(pytz.timezone("Europe/Paris")) + back = loads(dumps(after_dst)) + assert back == after_dst + assert back.tzinfo.zone == after_dst.tzinfo.zone + assert back.utcoffset() == after_dst.utcoffset()
