Skip to content

Commit

Permalink
Allow subclasses of UserObjects to be valid input even if they are de…
Browse files Browse the repository at this point in the history
…fined after the object that references them (#53)

* Allow subclasses of UserObjects to be valid input even if they are defined after the object that references them

* Refactor to remove redundant code paths
  • Loading branch information
jtv8 authored Jan 14, 2021
1 parent f39237e commit 4248f63
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 21 deletions.
14 changes: 7 additions & 7 deletions features/examples/modules/subclass_module.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, List
from typing import List

from abc import ABC, abstractmethod

Expand Down Expand Up @@ -28,14 +28,14 @@ def speak(self):
return f"{self.name}, the greyhound, says Woof!"


class Person(UserObject):
first_name: str = UserProperty(str)
last_name: str = UserProperty(str)
pets: List[Pet] = UserProperty(SchemaArray(Pet))


class Cat(Pet):
pet_type: str = UserProperty(SchemaConst("cat"))

def speak(self):
return f"{self.name} says Miaow!"


class Person(UserObject):
first_name: str = UserProperty(str)
last_name: str = UserProperty(str)
pets: List[Pet] = UserProperty(SchemaArray(Pet))
2 changes: 1 addition & 1 deletion wysdom/__version__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
VERSION = (0, 2, 1)
VERSION = (0, 2, 2)

__version__ = '.'.join(map(str, VERSION))
8 changes: 6 additions & 2 deletions wysdom/object_schema/SchemaAnyOf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ class SchemaAnyOf(Schema):
is referred to by other schemas.
"""

allowed_schemas: Tuple[Schema] = None
_allowed_schemas: Tuple[Schema] = None
schema_ref_name: Optional[str] = None

def __init__(
self,
allowed_schemas: Iterable[Schema],
schema_ref_name: Optional[str] = None
) -> None:
self.allowed_schemas = tuple(allowed_schemas)
self._allowed_schemas = tuple(allowed_schemas)
self.schema_ref_name = schema_ref_name

def __call__(
Expand All @@ -47,6 +47,10 @@ def __call__(
)
return valid_schemas[0](value, dom_info)

@property
def allowed_schemas(self) -> Tuple[Schema]:
return self._allowed_schemas

@property
def referenced_schemas(self) -> Dict[str, Schema]:
referenced_schemas = {}
Expand Down
47 changes: 36 additions & 11 deletions wysdom/user_objects/UserObject.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import Any, Type, Iterator, Union, Mapping
from typing import Any, Type, Iterator, Union, Mapping, List

import inspect

Expand Down Expand Up @@ -57,6 +57,38 @@ def _schema_superclasses(self) -> Iterator[Type[UserObject]]:
yield superclass


class SchemaAnyRegisteredSubclass(SchemaAnyOf):
"""
A SchemaAnyOf that allows any registered subclass of a UserObject.
Exists so that allowed_schemas can be populated dynamically and is not fixed
based on the object's current subclasses, but will also include any
future subclasses that are defined after this object's creation.
:param object_type: The UserObject subclass.
"""

def __init__(
self,
object_type: Type[UserObject]
):
super().__init__(
allowed_schemas=[],
schema_ref_name=f"{object_type.__module__}.{object_type.__name__}"
)
assert issubclass(object_type, RegistersSubclasses)
self.object_type = object_type

@property
def allowed_schemas(self) -> List[Schema]:
return [
subclass.__json_schema__()
for subclass_list in self.object_type.registered_subclasses().values()
for subclass in subclass_list
if issubclass(subclass, UserObject)
and not isinstance(subclass.__json_schema__(), SchemaAnyOf)
]


class UserObject(DOMObject):
"""
Base class for user-defined DOM objects.
Expand Down Expand Up @@ -110,16 +142,7 @@ def __json_schema__(cls) -> Schema:
if cls.registered_subclasses():
has_subclasses = True
if has_subclasses:
return SchemaAnyOf(
(
subclass.__json_schema__()
for subclass_list in cls.registered_subclasses().values()
for subclass in subclass_list
if issubclass(subclass, UserObject)
and not isinstance(subclass.__json_schema__(), SchemaAnyOf)
),
schema_ref_name=f"{cls.__module__}.{cls.__name__}"
)
return SchemaAnyRegisteredSubclass(cls)
else:
return SchemaObject(
properties=cls.__json_schema_properties__.properties,
Expand All @@ -128,3 +151,5 @@ def __json_schema__(cls) -> Schema:
object_type=cls,
schema_ref_name=f"{cls.__module__}.{cls.__name__}"
)


0 comments on commit 4248f63

Please sign in to comment.