From db24959e8461858db2b59a1859847c79ccec04c0 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Sun, 3 Mar 2024 17:21:49 -0800 Subject: [PATCH 1/5] Make `jsanitize(recursive_msonable=True)` respect duck typing "If it looks like a duck and quacks like a duck..." In this PR, I replace the `isinstance(obj, MSONable)` check with a `hasattr(obj, "as_dict")` check instead in `jsanitize`. This makes it possible to jsanitize objects that have been patched with `.as_dict()` attributes even if they are not formally inheriting from the `MSONable` class. --- monty/json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monty/json.py b/monty/json.py index ea1f6e3e..9a0a4d58 100644 --- a/monty/json.py +++ b/monty/json.py @@ -713,7 +713,7 @@ def jsanitize( except TypeError: pass - if recursive_msonable and isinstance(obj, MSONable): + if recursive_msonable and hasattr(obj, "as_dict") and callable(obj.as_dict): return obj.as_dict() if not strict: From c55b71f43f8e1b0b1ddf3d1532832f1fad1556d5 Mon Sep 17 00:00:00 2001 From: Andrew-S-Rosen Date: Sun, 3 Mar 2024 17:24:16 -0800 Subject: [PATCH 2/5] ruff --- monty/itertools.py | 1 + monty/operator.py | 1 + monty/os/path.py | 1 + monty/serialization.py | 1 + monty/shutil.py | 1 + monty/subprocess.py | 1 + monty/termcolor.py | 1 + tasks.py | 1 - 8 files changed, 7 insertions(+), 1 deletion(-) diff --git a/monty/itertools.py b/monty/itertools.py index 5d17c2fc..6980db7f 100644 --- a/monty/itertools.py +++ b/monty/itertools.py @@ -1,6 +1,7 @@ """ Additional tools for iteration. """ + import itertools try: diff --git a/monty/operator.py b/monty/operator.py index d153208c..45dfeb6f 100644 --- a/monty/operator.py +++ b/monty/operator.py @@ -1,6 +1,7 @@ """ Useful additional functions for operators """ + import operator diff --git a/monty/os/path.py b/monty/os/path.py index 65e628ca..8ffec360 100644 --- a/monty/os/path.py +++ b/monty/os/path.py @@ -1,6 +1,7 @@ """ Path based methods, e.g., which, zpath, etc. """ + import os from monty.fnmatch import WildCard diff --git a/monty/serialization.py b/monty/serialization.py index 5acbc43c..d2025c03 100644 --- a/monty/serialization.py +++ b/monty/serialization.py @@ -2,6 +2,7 @@ This module implements serialization support for common formats such as json and yaml. """ + import json import os diff --git a/monty/shutil.py b/monty/shutil.py index c008a601..1e19db3a 100644 --- a/monty/shutil.py +++ b/monty/shutil.py @@ -1,4 +1,5 @@ """Copying and zipping utilities. Works on directories mostly.""" + from __future__ import annotations import os diff --git a/monty/subprocess.py b/monty/subprocess.py index 869dfec1..98cb8edf 100644 --- a/monty/subprocess.py +++ b/monty/subprocess.py @@ -1,6 +1,7 @@ """ Calling shell processes. """ + import shlex import threading import traceback diff --git a/monty/termcolor.py b/monty/termcolor.py index 55a8541d..c3b0398b 100644 --- a/monty/termcolor.py +++ b/monty/termcolor.py @@ -20,6 +20,7 @@ ANSII Color formatting for output in terminal. """ + import os try: diff --git a/tasks.py b/tasks.py index 5c2e2a94..26078c09 100755 --- a/tasks.py +++ b/tasks.py @@ -4,7 +4,6 @@ Deployment file to facilitate releases of monty. """ - import datetime import glob import json From d44dc6aee1a7ce5b37ff5757fb4d02a4a6932e03 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Sun, 3 Mar 2024 17:25:58 -0800 Subject: [PATCH 3/5] Update json.py --- monty/json.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/monty/json.py b/monty/json.py index 9a0a4d58..f8cae750 100644 --- a/monty/json.py +++ b/monty/json.py @@ -713,8 +713,11 @@ def jsanitize( except TypeError: pass - if recursive_msonable and hasattr(obj, "as_dict") and callable(obj.as_dict): - return obj.as_dict() + if recursive_msonable: + try: + return obj.as_dict() + except AttributeError: + pass if not strict: return str(obj) From 0a098563a573517d922788b98fc63df44b03017e Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Sun, 3 Mar 2024 17:32:21 -0800 Subject: [PATCH 4/5] Update test_json.py --- tests/test_json.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_json.py b/tests/test_json.py index a6571a17..dd329f35 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -600,7 +600,7 @@ def test_jsanitize(self): assert clean["world"] is None assert json.loads(json.dumps(d)) == json.loads(json.dumps(clean)) - d = {"hello": GoodMSONClass(1, 2, 3)} + d = {"hello": GoodMSONClass(1, 2, 3), "test": "hi"} with pytest.raises(TypeError): json.dumps(d) clean = jsanitize(d) @@ -611,6 +611,7 @@ def test_jsanitize(self): clean_recursive_msonable = jsanitize(d, recursive_msonable=True) assert clean_recursive_msonable["hello"]["a"] == 1 assert clean_recursive_msonable["hello"]["b"] == 2 + assert clean_recursive_msonable["test"] == "hi" d = {"dt": datetime.datetime.now()} clean = jsanitize(d) From 5fbd4d77a92363f34ef2efa2fba311d6d0d230e3 Mon Sep 17 00:00:00 2001 From: "Andrew S. Rosen" Date: Sun, 3 Mar 2024 17:37:30 -0800 Subject: [PATCH 5/5] Update test_json.py --- tests/test_json.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_json.py b/tests/test_json.py index dd329f35..4e19911e 100644 --- a/tests/test_json.py +++ b/tests/test_json.py @@ -608,6 +608,7 @@ def test_jsanitize(self): clean_strict = jsanitize(d, strict=True) assert clean_strict["hello"]["a"] == 1 assert clean_strict["hello"]["b"] == 2 + assert clean_strict["test"] == "hi" clean_recursive_msonable = jsanitize(d, recursive_msonable=True) assert clean_recursive_msonable["hello"]["a"] == 1 assert clean_recursive_msonable["hello"]["b"] == 2