Skip to content

Commit d2dbc34

Browse files
authored
Merge pull request #3 from tofarr/use_injecty
Using Injecty instead of native DI
2 parents d94b041 + 0cdf55a commit d2dbc34

26 files changed

+356
-308
lines changed

.github/workflows/quality.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
- name: Install pytest code coverage
2828
run: python -m pip install ".[dev]"
2929
- name: testcoverage
30-
run: python -m pytest -n auto --cov-report=term-missing --cov=tests --cov=schemey --cov=marshy_config_schemey --cov=schemey_config_default --cov-fail-under=100
30+
run: python -m pytest -n auto --cov-report=term-missing --cov=tests --cov=schemey --cov=injecty_config_schemey --cov-fail-under=100
3131

3232
pylint:
3333
runs-on: ubuntu-latest
@@ -40,4 +40,4 @@ jobs:
4040
- name: Install pylint
4141
run: python -m pip install ".[dev]"
4242
- name: lint
43-
run: pylint marshy_config_schemey schemey schemey_config_default
43+
run: pylint injecty_config_schemey schemey

README.md

+8-12
Original file line numberDiff line numberDiff line change
@@ -62,24 +62,20 @@ This demonstrates starting with a json schema and generating python dataclasses
6262

6363
### Configuring the Context itself
6464

65-
Schemey uses a strategy very similar to marshy for configuration.
65+
Schemey uses [Injecty](https://github.org/tofarr/injecty) for configuration.
66+
The default configuration is [here](injecty_config_schemey/__init__.py)
6667

67-
Schemey looks for top level modules starting with the name `schemey_config_*`,
68-
sorts them by `priority`, and applies them to the default context by invoking their
69-
`configure` function. [schemey_config_default/__init__.py](schemey_config_default/__init__.py)
70-
contains the default set of factories.
71-
72-
For example, for a project named `no_more_uuids`, I may add a file `schemey_config_no_more_uuids/__init__.py`:
68+
For example, for a project named `no_more_uuids`, I may add a file `injecty_config_no_more_uuids/__init__.py`:
7369

7470
```
71+
from schemey.factory.schema_factory_abc import SchemaFactoryABC
72+
from schemey.factory.uuid_factory import UuidFactory
73+
7574
priority = 120 # Applied after default
7675
76+
7777
def configure(context):
78-
# For some reason, I don't want to be able to generate schemas for uuids!
79-
context.factories = [
80-
f for f in context.factories
81-
if 'uuid' not in f.__class__.__name__.lower()
82-
]
78+
context.deregister_impl(SchemaFactoryABC, UuidFactory)
8379
8480
```
8581

injecty_config_schemey/__init__.py

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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 (
15+
BoolTypeFactory,
16+
IntTypeFactory,
17+
NoneTypeFactory,
18+
FloatFactory,
19+
StrFactory,
20+
)
21+
from schemey.factory.tuple_schema_factory import TupleSchemaFactory
22+
from schemey.factory.uuid_factory import UuidFactory
23+
from schemey.json_schema.ranges_validator import RangesValidator
24+
from schemey.json_schema.schema_validator_abc import SchemaValidatorABC
25+
from schemey.json_schema.timestamp_validator import TimestampValidator
26+
from schemey.schema_marshaller import SchemaMarshaller
27+
28+
priority = 100
29+
30+
31+
def configure(context: InjectyContext):
32+
context.register_impl(MarshallerABC, SchemaMarshaller)
33+
context.register_impls(
34+
SchemaFactoryABC,
35+
[
36+
RefSchemaFactory,
37+
BoolTypeFactory,
38+
IntTypeFactory,
39+
NoneTypeFactory,
40+
FloatFactory,
41+
StrFactory,
42+
DatetimeFactory,
43+
UuidFactory,
44+
ArraySchemaFactory,
45+
TupleSchemaFactory,
46+
ExternalTypeFactory,
47+
DataclassSchemaFactory,
48+
EnumSchemaFactory,
49+
FactorySchemaFactory,
50+
ImplSchemaFactory,
51+
AnyOfSchemaFactory,
52+
],
53+
)
54+
55+
context.register_impls(
56+
SchemaValidatorABC,
57+
[
58+
RangesValidator,
59+
TimestampValidator,
60+
],
61+
)

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-4
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,9 +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(
99-
field_schema.python_type, default
100-
)
98+
default = context.marshy_context.load(field_schema.python_type, default)
10199
p = params_with_default
102100
else:
103101
p = params

schemey/factory/impl_schema_factory.py

+9-17
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
@@ -27,7 +26,10 @@ def from_type(
2726
path: str,
2827
ref_schemas: Dict[Type, Schema],
2928
) -> Optional[Schema]:
30-
impls = self.get_impls(type_, context)
29+
# noinspection PyTypeChecker
30+
impls = context.marshy_context.injecty_context.get_impls(
31+
type_, permit_no_impl=True
32+
)
3133
if impls:
3234
impls = sorted(list(impls), key=lambda i: i.__name__)
3335
any_of = []
@@ -55,17 +57,7 @@ def from_json(
5557
name = item.get("name")
5658
if not name or not item.get("anyOf"):
5759
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
60+
for base, _ in context.marshy_context.injecty_context.impls.items():
61+
if base.__name__ == name:
62+
schema = self.from_type(base, context, path, ref_schemas)
63+
return schema

schemey/factory/schema_factory_abc.py

+5-11
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
from abc import abstractmethod, ABC
2-
from functools import total_ordering
32
from typing import Type, Optional, Dict
43

54
from marshy.types import ExternalItemType
65

76
from schemey.schema import Schema
8-
from schemey.schema_context import SchemaContext
7+
8+
_SchemaContext = "schemey.schema_context.SchemaContext"
99

1010

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

1714
@abstractmethod
1815
def from_type(
1916
self,
2017
type_: Type,
21-
context: SchemaContext,
18+
context: _SchemaContext,
2219
path: str,
2320
ref_schemas: Dict[Type, Schema],
2421
) -> Optional[Schema]:
@@ -30,13 +27,10 @@ def from_type(
3027
def from_json(
3128
self,
3229
item: ExternalItemType,
33-
context: SchemaContext,
30+
context: _SchemaContext,
3431
path: str,
3532
ref_schemas: Dict[str, Schema],
3633
) -> Optional[Schema]:
3734
"""
3835
Create a schema for the type given, or return None if that was not possible
3936
"""
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.

0 commit comments

Comments
 (0)