Skip to content

Commit 09a5131

Browse files
Tim O'FarrellTim O'Farrell
Tim O'Farrell
authored and
Tim O'Farrell
committed
Using Injecty instead of native DI
1 parent d94b041 commit 09a5131

24 files changed

+324
-292
lines changed

injecty_config_schemey/__init__.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from injecty import InjectyContext
2+
from marshy.marshaller.marshaller_abc import MarshallerABC
3+
4+
from schemey.factory.any_of_schema_factory import AnyOfSchemaFactory
5+
from schemey.factory.array_schema_factory import ArraySchemaFactory
6+
from schemey.factory.dataclass_schema_factory import DataclassSchemaFactory
7+
from schemey.factory.datetime_factory import DatetimeFactory
8+
from schemey.factory.enum_schema_factory import EnumSchemaFactory
9+
from schemey.factory.external_type_factory import ExternalTypeFactory
10+
from schemey.factory.factory_schema_factory import FactorySchemaFactory
11+
from schemey.factory.impl_schema_factory import ImplSchemaFactory
12+
from schemey.factory.ref_schema_factory import RefSchemaFactory
13+
from schemey.factory.schema_factory_abc import SchemaFactoryABC
14+
from schemey.factory.simple_type_factory import BoolTypeFactory, IntTypeFactory, NoneTypeFactory, FloatFactory, \
15+
StrFactory
16+
from schemey.factory.tuple_schema_factory import TupleSchemaFactory
17+
from schemey.factory.uuid_factory import UuidFactory
18+
from schemey.json_schema.ranges_validator import RangesValidator
19+
from schemey.json_schema.schema_validator_abc import SchemaValidatorABC
20+
from schemey.json_schema.timestamp_validator import TimestampValidator
21+
from schemey.schema_marshaller import SchemaMarshaller
22+
23+
priority = 100
24+
25+
26+
def configure(context: InjectyContext):
27+
context.register_impl(MarshallerABC, SchemaMarshaller)
28+
context.register_impls(SchemaFactoryABC, [
29+
RefSchemaFactory,
30+
BoolTypeFactory,
31+
IntTypeFactory,
32+
NoneTypeFactory,
33+
FloatFactory,
34+
StrFactory,
35+
DatetimeFactory,
36+
UuidFactory,
37+
ArraySchemaFactory,
38+
TupleSchemaFactory,
39+
ExternalTypeFactory,
40+
DataclassSchemaFactory,
41+
EnumSchemaFactory,
42+
FactorySchemaFactory,
43+
ImplSchemaFactory,
44+
AnyOfSchemaFactory,
45+
])
46+
47+
context.register_impls(SchemaValidatorABC, [
48+
RangesValidator,
49+
TimestampValidator,
50+
])

marshy_config_schemey/__init__.py

-9
This file was deleted.

schemey/__init__.py

+3-24
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
import importlib
2-
import pkgutil
3-
from typing import Optional, Type
1+
from typing import Type
42

5-
from marshy import get_default_context
6-
from marshy.marshaller_context import MarshallerContext
73
from marshy.types import ExternalItemType
84

95
from schemey.schema import Schema
10-
from schemey.schema_context import SchemaContext
6+
from schemey.schema_context import SchemaContext, create_schema_context
117

128
_default_context = None
139
CONFIG_MODULE_PREFIX = "schemey_config_"
@@ -17,27 +13,10 @@
1713
def get_default_schema_context() -> SchemaContext:
1814
global _default_context
1915
if not _default_context:
20-
_default_context = new_default_schema_context()
16+
_default_context = create_schema_context()
2117
return _default_context
2218

2319

24-
def new_default_schema_context(
25-
marshaller_context: Optional[MarshallerContext] = None,
26-
) -> SchemaContext:
27-
if marshaller_context is None:
28-
marshaller_context = get_default_context()
29-
context = SchemaContext(marshaller_context=marshaller_context)
30-
# Set up context based on naming convention
31-
module_info = (
32-
m for m in pkgutil.iter_modules() if m.name.startswith(CONFIG_MODULE_PREFIX)
33-
)
34-
modules = [importlib.import_module(m.name) for m in module_info]
35-
modules.sort(key=lambda m: m.priority, reverse=True)
36-
for m in modules:
37-
getattr(m, "configure")(context)
38-
return context
39-
40-
4120
def schema_from_type(type_: Type) -> Schema:
4221
return get_default_schema_context().schema_from_type(type_)
4322

schemey/factory/dataclass_schema_factory.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def from_type(
4444
required.append(field.name)
4545
if field.default is not dataclasses.MISSING:
4646
field_schema = Schema({**field_schema.schema}, field_schema.python_type)
47-
field_schema.schema["default"] = context.marshaller_context.dump(
47+
field_schema.schema["default"] = context.marshy_context.dump(
4848
field.default, types[field.name]
4949
)
5050
properties[field.name] = field_schema.schema
@@ -95,7 +95,7 @@ def from_json(
9595
)
9696
default = field_item.get("default", dataclasses.MISSING)
9797
if default is not dataclasses.MISSING:
98-
default = context.marshaller_context.load(
98+
default = context.marshy_context.load(
9999
field_schema.python_type, default
100100
)
101101
p = params_with_default

schemey/factory/impl_schema_factory.py

+7-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from dataclasses import dataclass
2-
from typing import Type, Optional, Set, Dict
2+
from typing import Type, Optional, Dict
33

4-
from marshy.factory.impl_marshaller_factory import ImplMarshallerFactory
54
from marshy.types import ExternalItemType
65

76
from schemey.factory.schema_factory_abc import SchemaFactoryABC
@@ -17,7 +16,6 @@ class ImplSchemaFactory(SchemaFactoryABC):
1716
structure - though the UnionFactory will make a reasonably standardized class
1817
structure from the result.
1918
"""
20-
2119
priority: int = 150
2220

2321
def from_type(
@@ -27,7 +25,8 @@ def from_type(
2725
path: str,
2826
ref_schemas: Dict[Type, Schema],
2927
) -> Optional[Schema]:
30-
impls = self.get_impls(type_, context)
28+
# noinspection PyTypeChecker
29+
impls = context.marshy_context.injecty_context.get_impls(type_, permit_no_impl=True)
3130
if impls:
3231
impls = sorted(list(impls), key=lambda i: i.__name__)
3332
any_of = []
@@ -55,17 +54,7 @@ def from_json(
5554
name = item.get("name")
5655
if not name or not item.get("anyOf"):
5756
return
58-
factories = context.marshaller_context.get_factories()
59-
for factory in factories:
60-
if isinstance(factory, ImplMarshallerFactory):
61-
if factory.base.__name__ == name:
62-
schema = self.from_type(factory.base, context, path, ref_schemas)
63-
return schema
64-
65-
@staticmethod
66-
def get_impls(type_: Type, context: SchemaContext) -> Optional[Set[Type]]:
67-
factories = context.marshaller_context.get_factories()
68-
for factory in factories:
69-
if isinstance(factory, ImplMarshallerFactory):
70-
if factory.base == type_:
71-
return factory.impls
57+
for base, impls in context.marshy_context.injecty_context.impls.items():
58+
if base.__name__ == name:
59+
schema = self.from_type(base, context, path, ref_schemas)
60+
return schema

schemey/factory/schema_factory_abc.py

+5-10
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,18 @@
55
from marshy.types import ExternalItemType
66

77
from schemey.schema import Schema
8-
from schemey.schema_context import SchemaContext
8+
9+
_SchemaContext = "schemey.schema_context.SchemaContext"
910

1011

11-
@total_ordering
1212
class SchemaFactoryABC(ABC):
13-
@property
14-
def priority(self):
15-
return 100
13+
priority: int = 100
1614

1715
@abstractmethod
1816
def from_type(
1917
self,
2018
type_: Type,
21-
context: SchemaContext,
19+
context: _SchemaContext,
2220
path: str,
2321
ref_schemas: Dict[Type, Schema],
2422
) -> Optional[Schema]:
@@ -30,13 +28,10 @@ def from_type(
3028
def from_json(
3129
self,
3230
item: ExternalItemType,
33-
context: SchemaContext,
31+
context: _SchemaContext,
3432
path: str,
3533
ref_schemas: Dict[str, Schema],
3634
) -> Optional[Schema]:
3735
"""
3836
Create a schema for the type given, or return None if that was not possible
3937
"""
40-
41-
def __lt__(self, other):
42-
return self.priority < getattr(other, "priority", None)

schemey/factory/simple_type_factory.py

+31
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from dataclasses import dataclass
2+
from types import NoneType
23
from typing import Optional, Type, Dict
34

45
from marshy.types import ExternalItemType
@@ -34,3 +35,33 @@ def from_json(
3435
type_ = item.get("type")
3536
if type_ == self.json_type:
3637
return Schema(item, self.python_type)
38+
39+
40+
@dataclass
41+
class BoolTypeFactory(SimpleTypeFactory):
42+
python_type: Type = bool
43+
json_type: str = "boolean"
44+
45+
46+
@dataclass
47+
class IntTypeFactory(SimpleTypeFactory):
48+
python_type: Type = int
49+
json_type: str = "integer"
50+
51+
52+
@dataclass
53+
class NoneTypeFactory(SimpleTypeFactory):
54+
python_type: Type = NoneType
55+
json_type: str = "null"
56+
57+
58+
@dataclass
59+
class FloatFactory(SimpleTypeFactory):
60+
python_type: Type = float
61+
json_type: str = "number"
62+
63+
64+
@dataclass
65+
class StrFactory(SimpleTypeFactory):
66+
python_type: Type = str
67+
json_type: str = "string"

schemey/json_schema/__init__.py

-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +0,0 @@
1-
from typing import Callable, Dict
2-
3-
_custom_validators = {}
4-
5-
6-
def register_custom_json_schema_validator(property_name: str, validator: Callable):
7-
_custom_validators[property_name] = validator
8-
9-
10-
def get_custom_json_schema_validators() -> Dict[str, Callable]:
11-
return _custom_validators

schemey/json_schema/ranges.py

-43
This file was deleted.
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from jsonschema.exceptions import ValidationError
2+
3+
from schemey.json_schema.schema_validator_abc import SchemaValidatorABC
4+
5+
6+
class RangesValidator(SchemaValidatorABC):
7+
"""
8+
Add a custom validation rule for ranges to json object schema.
9+
e.g.:
10+
11+
{
12+
"type": "object",
13+
"properties": {
14+
"min_value": {"type": "integer", "minimum": 5},
15+
"max_value": {"type": "integer"}
16+
},
17+
"required": ["min_value", "max_value"],
18+
"ranges": [{
19+
"minProperty": "min_value",
20+
"maxProperty": "max_value",
21+
"allowEqual": false
22+
}]
23+
}
24+
"""
25+
property_name: str = "ranges"
26+
27+
def validate(self, validator, aP, instance, schema):
28+
if not validator.is_type(instance, "object"):
29+
return
30+
ranges_ = schema.get("ranges") or []
31+
for range_ in ranges_:
32+
min_property = range_["minProperty"]
33+
max_property = range_["maxProperty"]
34+
allow_equal = range_.get("allowEqual")
35+
min_value = instance.get(min_property)
36+
max_value = instance.get(max_property)
37+
if min_value < max_value:
38+
continue
39+
if allow_equal and min_value == max_value:
40+
continue
41+
# pylint: disable=R0801
42+
yield ValidationError(
43+
f"Value not in future: {instance}",
44+
validator=validator,
45+
validator_value=aP,
46+
instance=instance,
47+
schema=schema,
48+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from abc import ABC
2+
3+
4+
class SchemaValidatorABC(ABC):
5+
property_name: str
6+
7+
def validate(self, validator, aP, instance, schema):
8+
""" Validate this property """

0 commit comments

Comments
 (0)