Repository: radiasoft/pykern

Issue:
delete pkcollections.json_load_any
pkcollection.object_pairs_hooks should stay. json_load_any should be moved into pkjson. 

Depends on https://github.com/radiasoft/sirepo/issues/6442

Discussion:

Patch:
diff --git a/pykern/pkcli/rsmanifest.py b/pykern/pkcli/rsmanifest.py
index 62cf99d..9a05a03 100644
--- a/pykern/pkcli/rsmanifest.py
+++ b/pykern/pkcli/rsmanifest.py
@@ -4,7 +4,6 @@
 :copyright: Copyright (c) 2017 RadiaSoft LLC.  All Rights Reserved.
 :license: http://www.apache.org/licenses/LICENSE-2.0.html
 """
-from __future__ import absolute_import, division, print_function
 
 #: Appears in each directory
 BASENAME = "rsmanifest.json"
@@ -42,7 +41,7 @@ def add_code(name, version, uri, source_d, virtual_env=None, pyenv=None):
 
     fn = pkio.py_path(USER_FILE)
     try:
-        values = pkcollections.json_load_any(fn)
+        values = pkjson.load_any(fn)
     except Exception as e:
         if not (pkio.exception_is_not_found(e) or isinstance(e, ValueError)):
             raise
diff --git a/pykern/pkcollections.py b/pykern/pkcollections.py
index 4783e90..f71fe73 100644
--- a/pykern/pkcollections.py
+++ b/pykern/pkcollections.py
@@ -11,7 +11,6 @@ you to treat elements as attributes.
 import copy
 import collections.abc
 import decimal
-import json
 import types
 import pykern.pkcompat
 
@@ -364,43 +363,10 @@ Dict = PKDict
 DictNameError = PKDictNameError
 
 
-_JSON_INT_MAX = 2**53 - 1
-_JSON_INT_MIN = -(2**53) + 1
-
-
-def json_load_any(obj, *args, **kwargs):
-    """Parse json with `PKDict` for object pairs
-
-    Args:
-        obj (object): str or object with "read" or py.path
-        args (tuple): passed verbatim
-        kwargs (dict): object_pairs_hook may be overriden
-
-    Returns:
-        object: parsed JSON
-    """
-
-    def _parse_int(value):
-        if len(value) > 64:
-            # 64 is pretty arbitrary, but reasonable in Python. Nothing in
-            # JSON can be this large.
-            raise ValueError(f"number={value:60s}... unreasonably large")
-        if len(value) > 17:
-            return float(value)
-        res = int(value)
-        if _JSON_INT_MIN <= res <= _JSON_INT_MAX:
-            return res
-        return float(res)
-
-    kwargs.setdefault("parse_int", _parse_int)
-    kwargs.setdefault("object_pairs_hook", object_pairs_hook)
-    return json.loads(obj.read() if hasattr(obj, "read") else obj, *args, **kwargs)
-
-
 def object_pairs_hook(*args, **kwargs):
     """Tries to use `PKDict` if PKDictNameError uses `dict`
 
-    Usefule for json, msgpack, etc.
+    Useful for json, msgpack, etc.
 
     Returns:
         object: `PKDict` or `dict`
diff --git a/pykern/pkjson.py b/pykern/pkjson.py
index 7510f8e..5d3a736 100644
--- a/pykern/pkjson.py
+++ b/pykern/pkjson.py
@@ -13,10 +13,12 @@ ENCODING = "utf-8"
 #: MIME type
 MIME_TYPE = "application/json"
 
-
 #: Content-Type MIME header
 CONTENT_TYPE = f'{MIME_TYPE}; charset="{ENCODING}"'
 
+_JSON_INT_MAX = 2**53 - 1
+_JSON_INT_MIN = -(2**53) + 1
+
 
 class Encoder(json.JSONEncoder):
     def default(self, obj):
@@ -84,15 +86,33 @@ def dump_str(obj, **kwargs):
     return dump_pretty(obj, pretty=False, **kwargs)
 
 
-def load_any(obj):
-    """Calls `pkcollections.json_load_any`
+def load_any(obj, *args, **kwargs):
+    """Parse object containing json into dict-like object.
+
+    object_pairs_hook modifies the return type.
 
     Args:
-        obj (object): str or object with "read"
+        obj (object): str or object with "read" or py.path
+        args (tuple): passed verbatim to json.loads()
+        kwargs (dict): object_pairs_hook may be overriden
 
     Returns:
         object: parsed JSON
     """
     from pykern import pkcollections
 
-    return pkcollections.json_load_any(obj)
+    def _parse_int(value):
+        if len(value) > 64:
+            # 64 is pretty arbitrary, but reasonable in Python. Nothing in
+            # JSON can be this large.
+            raise ValueError(f"number={value:60s}... unreasonably large")
+        if len(value) > 17:
+            return float(value)
+        res = int(value)
+        if _JSON_INT_MIN <= res <= _JSON_INT_MAX:
+            return res
+        return float(res)
+
+    kwargs.setdefault("parse_int", _parse_int)
+    kwargs.setdefault("object_pairs_hook", pkcollections.object_pairs_hook)
+    return json.loads(obj.read() if hasattr(obj, "read") else obj, *args, **kwargs)


Test patch:
diff --git a/tests/pkcollections_test.py b/tests/pkcollections_test.py
index c5b19ce..59593c5 100644
--- a/tests/pkcollections_test.py
+++ b/tests/pkcollections_test.py
@@ -197,25 +197,6 @@ def test_dict_pkupdate():
     pkeq(1, d.a)
 
 
-def test_json_load_any():
-    """Validate json_load_any()"""
-    import json
-    from pykern import pkcollections
-    from pykern.pkunit import pkeq
-
-    j = json.dumps({"a": 33})
-    j2 = pkcollections.json_load_any(j)
-    pkeq(
-        33,
-        j2.a,
-        "{}: j2.a is not 33",
-        j2.a,
-    )
-    j = json.dumps({"a": 33, "b": {"values": "will collide, but ok"}})
-    j2 = pkcollections.json_load_any(j)
-    pkcollections.json_load_any(j, object_pairs_hook=pkcollections.PKDict)
-
-
 def test_pkmerge():
     from pykern.pkunit import pkeq
     from pykern.pkcollections import PKDict
diff --git a/tests/pkjson_test.py b/tests/pkjson_test.py
index 71ec670..ceae085 100644
--- a/tests/pkjson_test.py
+++ b/tests/pkjson_test.py
@@ -8,12 +8,24 @@
 
 def test_load_any():
     import json
+    from pykern import pkcollections
     from pykern import pkjson
     from pykern.pkunit import pkeq
 
     j = json.dumps(["a", "b"])
     j2 = pkjson.load_any(j)
     pkeq("a", j2[0])
+    j3 = json.dumps({"a": 33})
+    j4 = pkjson.load_any(j3)
+    pkeq(
+        33,
+        j4.a,
+        "{}: j4.a is not 33",
+        j4.a,
+    )
+    j5 = json.dumps({"a": 33, "b": {"values": "will collide, but ok"}})
+    j6 = pkjson.load_any(j5)
+    pkjson.load_any(j5, object_pairs_hook=pkcollections.PKDict)
 
 
 def test_load_any_parse_int():