diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 0000000..277f51e --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,15 @@ +name: Test + +on: [push, pull_request] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + + steps: + - name: Run pytest + uses: cclauss/GitHub-Action-for-pytest@0.5.0 \ No newline at end of file diff --git a/pykson/__init__.py b/pykson/__init__.py index 6df16e9..f7e52e8 100644 --- a/pykson/__init__.py +++ b/pykson/__init__.py @@ -623,9 +623,7 @@ def __get_fields(cls) -> List[Field]: fields_list.append(field) for base in cls.__bases__: base_type_dicts = base.__dict__ # type(self).__dict__ - for n, field in base_type_dicts.items(): - if isinstance(field, Field): - fields_list.append(field) + fields_list += JsonObjectMeta.__get_fields(base) return fields_list @staticmethod @@ -725,18 +723,32 @@ def __init__(self, accept_unknown: bool = False, extra_attributes: Optional[List # Empty init will be replaced by meta class super(JsonObject, self).__init__() + def __str__(self): + return json.dumps(json.loads(Pykson().to_json(self)), indent=2, sort_keys=True) T = TypeVar('T', bound=JsonObject) class ObjectField(Field): def __set__(self, instance, value, test: bool = False): + if isinstance(value, dict) and issubclass(self.item_type, JsonObject): + super().__set__(instance, Pykson().from_json(value, self.item_type), test) + return if value is not None and not isinstance(value, self.item_type): raise TypeError(instance, self.name, self.item_type, value) super().__set__(instance, value, test) - def __init__(self, item_type: Type[T], serialized_name: Optional[str] = None, null: bool = True): - super(ObjectField, self).__init__(field_type=FieldType.LIST, serialized_name=serialized_name, null=null) + def __init__(self, + item_type: Type[T], + serialized_name: Optional[str] = None, + null: bool = True, + default_value: Optional[Any] = None): + super(ObjectField, self).__init__( + field_type=FieldType.LIST, + serialized_name=serialized_name, + null=null, + default_value=default_value) + assert default_value is None or isinstance(default_value, JsonObject) self.item_type = item_type @@ -751,6 +763,9 @@ def __set__(self, instance, value, test: bool = False): value = [] for item in value: assert item is not None, "Null item passed to ObjectListField" + if not isinstance(item, self.item_type) and isinstance(item, dict) and issubclass(self.item_type, JsonObject): + super(ObjectListField, self).__set__(instance, Pykson().from_json(value, self.item_type), test) + return assert isinstance(item, self.item_type), "ObjectListField items must be of " + str( self.item_type) + ", found " + str(type(item)) super(ObjectListField, self).__set__(instance, value, test) @@ -1062,7 +1077,9 @@ def _to_json(self, item: Union[T, List[T]], serialized_keys_based: bool = True) final_dict[field_key] = field_value return final_dict - def to_json(self, item: Union[T, List[T]]) -> str: + def to_json(self, item: Union[T, List[T]], indent: bool = True) -> str: + if indent: + return json.dumps(self._to_json(item), indent=2, sort_keys=True) return json.dumps(self._to_json(item)) def to_dict_or_list(self, item: Union[T, List[T]]) -> Union[Dict[str, Any], List[Dict[str, Any]]]: diff --git a/setup.py b/setup.py index 862aec4..5be6d6d 100644 --- a/setup.py +++ b/setup.py @@ -7,9 +7,9 @@ required = f.read().splitlines() setuptools.setup(name='pykson', - version='0.9.9.8.7', - author='Sina Rezaei', - author_email='sinarezaei1991@gmail.com', + version='1.0.2', + author='Sina Rezaei, Patrick J. Pereira', + author_email='sinarezaei1991@gmail.com, patrickelectric@gmail.com', long_description_content_type="text/markdown", long_description=long_description, description='Pykson: A JSON Serializer/Deserializer for Python', diff --git a/test_pykson.py b/test_pykson.py new file mode 100644 index 0000000..2ec6ce5 --- /dev/null +++ b/test_pykson.py @@ -0,0 +1,20 @@ +import pykson + +class Class1(pykson.JsonObject): + variable_1 = pykson.IntegerField(default_value=1) + +class Class2(Class1): + variable_2 = pykson.IntegerField(default_value=2) + +class Class3(Class2): + variable_3 = pykson.IntegerField(default_value=3) + +class Class4(Class3): + variable_4 = pykson.IntegerField(default_value=4) + +def test_multiple_inheritance(): + class4 = Class4() + assert class4.variable_1 == 1 + assert class4.variable_2 == 2 + assert class4.variable_3 == 3 + assert class4.variable_4 == 4 \ No newline at end of file