Skip to content

Commit

Permalink
Merge pull request #14 from robabram/ra/dict-annot-type-fix
Browse files Browse the repository at this point in the history
Correctly handle 'typing.Dict' annotations
  • Loading branch information
robabram authored Mar 27, 2023
2 parents 2500124 + b55820b commit af1f747
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 11 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from setuptools import setup, find_packages

__VERSION__ = "1.0.18"
__VERSION__ = "1.1.0"

base_dir = os.path.abspath(os.path.dirname(__file__))

Expand Down
19 changes: 9 additions & 10 deletions src/python_easy_json/json_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import datetime
import inspect
import json
import sys
import typing

from collections import OrderedDict
Expand All @@ -27,14 +26,13 @@ def _get_annot_cls(annots: dict, key: str):
:param key: Key to lookup in annots.
:return: Annotation class or JSONObject class
"""
cls_ = JSONObject
cls_ = None
if key in annots:
cls_ = annots[key]
# See if this annotation is a list of objects, if so, get the first
# available object type in the list.
if hasattr(cls_, '_name') and hasattr(cls_, '__args__') and cls_._name == 'List':
if hasattr(cls_, '_name') and cls_._name == 'List' and hasattr(cls_, '__args__'):
cls_ = cls_.__args__[0]

# Check if typing annotation class is a Union type.
# Try to find the right object class in the Union types list, ignore 'builtin' types.
if '__args__' in cls_.__dict__ and isinstance(cls_.__dict__['__args__'], (list, tuple)):
Expand All @@ -43,7 +41,12 @@ def _get_annot_cls(annots: dict, key: str):
if issubclass(type(cls_item), object):
if cls_item.__module__ == 'builtins':
continue
return cls_item
cls_ = cls_item
break
# Fallback to JSONObject if needed
if not cls_ or cls_.__module__ == 'typing':
cls_ = JSONObject

return cls_

def _collect_annotations(self, cls_: object):
Expand All @@ -64,7 +67,6 @@ def _collect_annotations(self, cls_: object):

return annots


def __init__(self, data: typing.Union[typing.Dict, str, None] = None, cast_types: bool = False,
ordered: bool = False):
"""
Expand Down Expand Up @@ -100,10 +102,7 @@ def __init__(self, data: typing.Union[typing.Dict, str, None] = None, cast_types
_tmp = list()
for i in v:
if isinstance(i, dict):
try:
_tmp.append(self._get_annot_cls(annots, k)(i, cast_types=cast_types, ordered=ordered))
except TypeError:
_tmp.append(JSONObject(i, cast_types=cast_types, ordered=ordered))
_tmp.append(self._get_annot_cls(annots, k)(i, cast_types=cast_types, ordered=ordered))
elif isinstance(i, str):
try:
_tmp_data = json.loads(i)
Expand Down
61 changes: 61 additions & 0 deletions tests/test_json_with_dicts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#
# This file is subject to the terms and conditions defined in the
# file 'LICENSE', which is part of this source code package.
#
from typing import Dict, List

from tests.base_test import BaseTestCase
from python_easy_json import JSONObject


class SimpleDictTestObject(JSONObject):

test_prop: Dict = None


class ListDictTestObject(JSONObject):

test_prop: List[Dict] = None


class TestListsDict(BaseTestCase):

def test_simple_dict(self):
""" Test an object with a typing.Dict type hint. """

data = {
'test_prop': {
'col1': 'abc',
'col2': 'xyz'
}
}

obj = SimpleDictTestObject(data, cast_types=True)

self.assertIsInstance(obj, SimpleDictTestObject)
# Ensure property type is 'dict'.
self.assertIsInstance(obj.test_prop, JSONObject)

def test_list_of_dicts(self):
""" Test a list of dict objects """
data = {
'test_prop': [
{
'col1': 'abc',
'col2': 'xyz'
},
{
'col1': 'ghi',
'col2': 'rst'
}
]
}

obj = ListDictTestObject(data, cast_types=True)

self.assertIsInstance(obj, ListDictTestObject)
# Ensure property type is 'dict'.
self.assertIsInstance(obj.test_prop, list)

for v in obj.test_prop:
self.assertIsInstance(v, JSONObject)

0 comments on commit af1f747

Please sign in to comment.