[Python-checkins] [3.12] Revert "GH-96145: Add AttrDict to JSON module for use with object_hook (GH-96146)" (GH-105948) (#106117)

Yhg1s webhook-mailer at python.org
Mon Jun 26 15:11:53 EDT 2023


https://github.com/python/cpython/commit/1acfecbc0041b8a3b6c017d23ebed55aba39de6b
commit: 1acfecbc0041b8a3b6c017d23ebed55aba39de6b
branch: 3.12
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: Yhg1s <thomas at python.org>
date: 2023-06-26T21:11:49+02:00
summary:

[3.12] Revert "GH-96145: Add AttrDict to JSON module for use with object_hook (GH-96146)" (GH-105948) (#106117)

Revert "GH-96145: Add AttrDict to JSON module for use with object_hook (GH-96146)" (GH-105948)

This reverts commit 1f0eafa844bf5a380603d55e8d4b42d8c2a3439d.
(cherry picked from commit d3af83b9342457d8b24476baeb799f7506ff04f3)

Co-authored-by: Łukasz Langa <lukasz at langa.pl>

files:
A Misc/NEWS.d/next/Library/2023-06-20-23-18-45.gh-issue-96145.o5dTRM.rst
D Lib/test/test_json/test_attrdict.py
M Doc/library/json.rst
M Doc/whatsnew/3.12.rst
M Lib/json/__init__.py
M Lib/test/test_json/__init__.py

diff --git a/Doc/library/json.rst b/Doc/library/json.rst
index ef58dd0942364..5383614575c21 100644
--- a/Doc/library/json.rst
+++ b/Doc/library/json.rst
@@ -9,11 +9,6 @@
 
 **Source code:** :source:`Lib/json/__init__.py`
 
-.. testsetup:: *
-
-   import json
-   from json import AttrDict
-
 --------------
 
 `JSON (JavaScript Object Notation) <https://json.org>`_, specified by
@@ -548,44 +543,6 @@ Exceptions
 
    .. versionadded:: 3.5
 
-.. class:: AttrDict(**kwargs)
-           AttrDict(mapping, **kwargs)
-           AttrDict(iterable, **kwargs)
-
-   Subclass of :class:`dict` that also supports attribute style dotted access.
-
-   This class is intended for use with the :attr:`object_hook` in
-   :func:`json.load` and :func:`json.loads`:
-
-   .. doctest::
-
-       >>> json_string = '{"mercury": 88, "venus": 225, "earth": 365, "mars": 687}'
-       >>> orbital_period = json.loads(json_string, object_hook=AttrDict)
-       >>> orbital_period['earth']     # Dict style lookup
-       365
-       >>> orbital_period.earth        # Attribute style lookup
-       365
-       >>> orbital_period.keys()       # All dict methods are present
-       dict_keys(['mercury', 'venus', 'earth', 'mars'])
-
-   Attribute style access only works for keys that are valid attribute
-   names.  In contrast, dictionary style access works for all keys.  For
-   example, ``d.two words`` contains a space and is not syntactically
-   valid Python, so ``d["two words"]`` should be used instead.
-
-   If a key has the same name as a dictionary method, then a dictionary
-   lookup finds the key and an attribute lookup finds the method:
-
-   .. doctest::
-
-        >>> d = AttrDict(items=50)
-        >>> d['items']                  # Lookup the key
-        50
-        >>> d.items()                   # Call the method
-        dict_items([('items', 50)])
-
-   .. versionadded:: 3.12
-
 
 Standard Compliance and Interoperability
 ----------------------------------------
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index e06faf3ccaa1a..782a5013e869c 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -593,14 +593,6 @@ itertools
   tuples where the last batch may be shorter than the rest.
   (Contributed by Raymond Hettinger in :gh:`98363`.)
 
-json
-----
-
-* Added :class:`json.AttrDict` for use with ``object_hook`` in :func:`json.load`
-  or :func:`json.loads`.  This is a subclass of :class:`dict` that also supports
-  attribute style dotted access.
-  (Contributed by Raymond Hettinger in :gh:`96145`.)
-
 math
 ----
 
diff --git a/Lib/json/__init__.py b/Lib/json/__init__.py
index 256e76a0a67f8..ed2c74771ea87 100644
--- a/Lib/json/__init__.py
+++ b/Lib/json/__init__.py
@@ -97,7 +97,7 @@
 """
 __version__ = '2.0.9'
 __all__ = [
-    'dump', 'dumps', 'load', 'loads', 'AttrDict',
+    'dump', 'dumps', 'load', 'loads',
     'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
 ]
 
@@ -357,53 +357,3 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None,
     if parse_constant is not None:
         kw['parse_constant'] = parse_constant
     return cls(**kw).decode(s)
-
-class AttrDict(dict):
-    """Dict like object that supports attribute style dotted access.
-
-    This class is intended for use with the *object_hook* in json.loads():
-
-        >>> from json import loads, AttrDict
-        >>> json_string = '{"mercury": 88, "venus": 225, "earth": 365, "mars": 687}'
-        >>> orbital_period = loads(json_string, object_hook=AttrDict)
-        >>> orbital_period['earth']     # Dict style lookup
-        365
-        >>> orbital_period.earth        # Attribute style lookup
-        365
-        >>> orbital_period.keys()       # All dict methods are present
-        dict_keys(['mercury', 'venus', 'earth', 'mars'])
-
-    Attribute style access only works for keys that are valid attribute names.
-    In contrast, dictionary style access works for all keys.
-    For example, ``d.two words`` contains a space and is not syntactically
-    valid Python, so ``d["two words"]`` should be used instead.
-
-    If a key has the same name as dictionary method, then a dictionary
-    lookup finds the key and an attribute lookup finds the method:
-
-        >>> d = AttrDict(items=50)
-        >>> d['items']                  # Lookup the key
-        50
-        >>> d.items()                   # Call the method
-        dict_items([('items', 50)])
-
-    """
-    __slots__ = ()
-
-    def __getattr__(self, attr):
-        try:
-            return self[attr]
-        except KeyError:
-            raise AttributeError(attr) from None
-
-    def __setattr__(self, attr, value):
-        self[attr] = value
-
-    def __delattr__(self, attr):
-        try:
-            del self[attr]
-        except KeyError:
-            raise AttributeError(attr) from None
-
-    def __dir__(self):
-        return list(self) + dir(type(self))
diff --git a/Lib/test/test_json/__init__.py b/Lib/test/test_json/__init__.py
index 37b2e0d5e26d1..74b64ed86a318 100644
--- a/Lib/test/test_json/__init__.py
+++ b/Lib/test/test_json/__init__.py
@@ -18,7 +18,6 @@ class PyTest(unittest.TestCase):
     json = pyjson
     loads = staticmethod(pyjson.loads)
     dumps = staticmethod(pyjson.dumps)
-    AttrDict = pyjson.AttrDict
     JSONDecodeError = staticmethod(pyjson.JSONDecodeError)
 
 @unittest.skipUnless(cjson, 'requires _json')
diff --git a/Lib/test/test_json/test_attrdict.py b/Lib/test/test_json/test_attrdict.py
deleted file mode 100644
index 143ea462d310a..0000000000000
--- a/Lib/test/test_json/test_attrdict.py
+++ /dev/null
@@ -1,145 +0,0 @@
-from test.test_json import PyTest
-import pickle
-import sys
-import unittest
-
-kepler_dict = {
-    "orbital_period": {
-        "mercury": 88,
-        "venus": 225,
-        "earth": 365,
-        "mars": 687,
-        "jupiter": 4331,
-        "saturn": 10_756,
-        "uranus": 30_687,
-        "neptune": 60_190,
-    },
-    "dist_from_sun": {
-        "mercury": 58,
-        "venus": 108,
-        "earth": 150,
-        "mars": 228,
-        "jupiter": 778,
-        "saturn": 1_400,
-        "uranus": 2_900,
-        "neptune": 4_500,
-    }
-}
-
-class TestAttrDict(PyTest):
-
-    def test_dict_subclass(self):
-        self.assertTrue(issubclass(self.AttrDict, dict))
-
-    def test_slots(self):
-        d = self.AttrDict(x=1, y=2)
-        with self.assertRaises(TypeError):
-            vars(d)
-
-    def test_constructor_signatures(self):
-        AttrDict = self.AttrDict
-        target = dict(x=1, y=2)
-        self.assertEqual(AttrDict(x=1, y=2), target)                   # kwargs
-        self.assertEqual(AttrDict(dict(x=1, y=2)), target)             # mapping
-        self.assertEqual(AttrDict(dict(x=1, y=0), y=2), target)        # mapping, kwargs
-        self.assertEqual(AttrDict([('x', 1), ('y', 2)]), target)       # iterable
-        self.assertEqual(AttrDict([('x', 1), ('y', 0)], y=2), target)  # iterable, kwargs
-
-    def test_getattr(self):
-        d = self.AttrDict(x=1, y=2)
-        self.assertEqual(d.x, 1)
-        with self.assertRaises(AttributeError):
-            d.z
-
-    def test_setattr(self):
-        d = self.AttrDict(x=1, y=2)
-        d.x = 3
-        d.z = 5
-        self.assertEqual(d, dict(x=3, y=2, z=5))
-
-    def test_delattr(self):
-        d = self.AttrDict(x=1, y=2)
-        del d.x
-        self.assertEqual(d, dict(y=2))
-        with self.assertRaises(AttributeError):
-            del d.z
-
-    def test_dir(self):
-        d = self.AttrDict(x=1, y=2)
-        self.assertTrue(set(dir(d)), set(dir(dict)).union({'x', 'y'}))
-
-    def test_repr(self):
-        # This repr is doesn't round-trip.  It matches a regular dict.
-        # That seems to be the norm for AttrDict recipes being used
-        # in the wild.  Also it supports the design concept that an
-        # AttrDict is just like a regular dict but has optional
-        # attribute style lookup.
-        self.assertEqual(repr(self.AttrDict(x=1, y=2)),
-                         repr(dict(x=1, y=2)))
-
-    def test_overlapping_keys_and_methods(self):
-        d = self.AttrDict(items=50)
-        self.assertEqual(d['items'], 50)
-        self.assertEqual(d.items(), dict(d).items())
-
-    def test_invalid_attribute_names(self):
-        d = self.AttrDict({
-            'control': 'normal case',
-            'class': 'keyword',
-            'two words': 'contains space',
-            'hypen-ate': 'contains a hyphen'
-        })
-        self.assertEqual(d.control, dict(d)['control'])
-        self.assertEqual(d['class'], dict(d)['class'])
-        self.assertEqual(d['two words'], dict(d)['two words'])
-        self.assertEqual(d['hypen-ate'], dict(d)['hypen-ate'])
-
-    def test_object_hook_use_case(self):
-        AttrDict = self.AttrDict
-        json_string = self.dumps(kepler_dict)
-        kepler_ad = self.loads(json_string, object_hook=AttrDict)
-
-        self.assertEqual(kepler_ad, kepler_dict)     # Match regular dict
-        self.assertIsInstance(kepler_ad, AttrDict)   # Verify conversion
-        self.assertIsInstance(kepler_ad.orbital_period, AttrDict)  # Nested
-
-        # Exercise dotted lookups
-        self.assertEqual(kepler_ad.orbital_period, kepler_dict['orbital_period'])
-        self.assertEqual(kepler_ad.orbital_period.earth,
-                         kepler_dict['orbital_period']['earth'])
-        self.assertEqual(kepler_ad['orbital_period'].earth,
-                         kepler_dict['orbital_period']['earth'])
-
-        # Dict style error handling and Attribute style error handling
-        with self.assertRaises(KeyError):
-            kepler_ad.orbital_period['pluto']
-        with self.assertRaises(AttributeError):
-            kepler_ad.orbital_period.Pluto
-
-        # Order preservation
-        self.assertEqual(list(kepler_ad.items()), list(kepler_dict.items()))
-        self.assertEqual(list(kepler_ad.orbital_period.items()),
-                             list(kepler_dict['orbital_period'].items()))
-
-        # Round trip
-        self.assertEqual(self.dumps(kepler_ad), json_string)
-
-    def test_pickle(self):
-        AttrDict = self.AttrDict
-        json_string = self.dumps(kepler_dict)
-        kepler_ad = self.loads(json_string, object_hook=AttrDict)
-
-        # Pickling requires the cached module to be the real module
-        cached_module = sys.modules.get('json')
-        sys.modules['json'] = self.json
-        try:
-            for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
-                kepler_ad2 = pickle.loads(pickle.dumps(kepler_ad, protocol))
-                self.assertEqual(kepler_ad2, kepler_ad)
-                self.assertEqual(type(kepler_ad2), AttrDict)
-        finally:
-            sys.modules['json'] = cached_module
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2023-06-20-23-18-45.gh-issue-96145.o5dTRM.rst b/Misc/NEWS.d/next/Library/2023-06-20-23-18-45.gh-issue-96145.o5dTRM.rst
new file mode 100644
index 0000000000000..f4fb0e46ce5e5
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-06-20-23-18-45.gh-issue-96145.o5dTRM.rst
@@ -0,0 +1 @@
+Reverted addition of ``json.AttrDict``.



More information about the Python-checkins mailing list