Skip to content

Commit b4a029f

Browse files
committed
feat: fix all typing issues raised by mypy
1 parent 6ce5897 commit b4a029f

6 files changed

Lines changed: 61 additions & 30 deletions

File tree

.pre-commit-config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ repos:
1919
rev: v1.15.0
2020
hooks:
2121
- id: mypy
22+
additional_dependencies:
23+
- pydantic[email]>=2.7.0
2224
- repo: https://github.com/codespell-project/codespell
2325
rev: v2.4.1
2426
hooks:

pyproject.toml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,16 @@ docstring-code-format = true
105105
addopts = "--doctest-modules --doctest-glob='*.rst'"
106106
doctest_optionflags= "ALLOW_UNICODE IGNORE_EXCEPTION_DETAIL ELLIPSIS"
107107

108-
# [tool.mypy]
109-
# plugins = [
110-
# "pydantic.mypy"
111-
# ]
108+
[tool.mypy]
109+
plugins = [
110+
"pydantic.mypy"
111+
]
112+
warn_unused_ignores = true
113+
114+
[tool.pydantic-mypy]
115+
init_forbid_extra = true
116+
init_typed = true
117+
warn_required_dynamic_aliases = true
112118

113119
[tool.tox]
114120
requires = ["tox>=4.19"]

scim2_models/rfc7643/resource.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,18 +174,12 @@ def __class_getitem__(cls, item: Any) -> type["Resource"]:
174174
f"{cls.__name__}[{', '.join(ext.__name__ for ext in valid_extensions)}]"
175175
)
176176

177-
class_attrs = {
178-
"__scim_extension_metadata__": {
179-
"args": (item,),
180-
"origin": cls,
181-
"extensions": valid_extensions,
182-
}
183-
}
177+
class_attrs = {"__scim_extension_metadata__": valid_extensions}
184178

185179
for extension in valid_extensions:
186180
schema = extension.model_fields["schemas"].default[0]
187181
class_attrs[extension.__name__] = Field(
188-
None,
182+
default=None, # type: ignore[arg-type]
189183
serialization_alias=schema,
190184
validation_alias=normalize_attribute_name(schema),
191185
)
@@ -424,6 +418,10 @@ def model_attribute_to_scim_attribute(
424418

425419
field_info = model.model_fields[attribute_name]
426420
root_type = model.get_field_root_type(attribute_name)
421+
if root_type is None:
422+
raise ValueError(
423+
f"Could not determine root type for attribute {attribute_name}"
424+
)
427425
attribute_type = Attribute.Type.from_python(root_type)
428426
sub_attributes = (
429427
[
@@ -443,7 +441,7 @@ def model_attribute_to_scim_attribute(
443441

444442
return Attribute(
445443
name=field_info.serialization_alias or attribute_name,
446-
type=attribute_type,
444+
type=Attribute.Type(attribute_type),
447445
multi_valued=model.get_field_multiplicity(attribute_name),
448446
description=field_info.description,
449447
canonical_values=field_info.examples,

scim2_models/rfc7643/resource_type.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,13 @@ def from_resource(cls, resource_model: type[Resource]) -> Self:
8484
name = schema.split(":")[-1]
8585

8686
# Get extensions from the metadata system
87-
if hasattr(resource_model, "__scim_extension_metadata__"):
88-
extensions = resource_model.__scim_extension_metadata__["extensions"]
89-
else:
90-
extensions = []
87+
extensions = getattr(resource_model, "__scim_extension_metadata__", [])
9188

9289
return cls(
9390
id=name,
9491
name=name,
9592
description=name,
96-
endpoint=f"/{name}s",
93+
endpoint=f"/{name}s", # type: ignore[arg-type]
9794
schema_=schema,
9895
schema_extensions=[
9996
SchemaExtension(

scim2_models/rfc7643/schema.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,25 +126,25 @@ def to_python(
126126
return attr_types[self.value]
127127

128128
@classmethod
129-
def from_python(cls, pytype) -> str:
129+
def from_python(cls, pytype: type) -> "Attribute.Type":
130130
if get_origin(pytype) == Reference:
131-
return cls.reference.value
131+
return cls.reference
132132

133-
if is_complex_attribute(pytype):
134-
return cls.complex.value
133+
if pytype and is_complex_attribute(pytype):
134+
return cls.complex
135135

136136
if pytype in (Required, CaseExact):
137-
return cls.boolean.value
137+
return cls.boolean
138138

139139
attr_types = {
140-
str: cls.string.value,
141-
bool: cls.boolean.value,
142-
float: cls.decimal.value,
143-
int: cls.integer.value,
144-
datetime: cls.date_time.value,
145-
Base64Bytes: cls.binary.value,
140+
str: cls.string,
141+
bool: cls.boolean,
142+
float: cls.decimal,
143+
int: cls.integer,
144+
datetime: cls.date_time,
145+
Base64Bytes: cls.binary,
146146
}
147-
return attr_types.get(pytype, cls.string.value)
147+
return attr_types.get(pytype, cls.string)
148148

149149
name: Annotated[
150150
Optional[str], Mutability.read_only, Required.true, CaseExact.true

tests/test_resource_extension.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,3 +320,31 @@ def test_class_getitem():
320320

321321
with pytest.raises(TypeError, match="is not a valid Extension type"):
322322
User[int]
323+
324+
325+
def test_model_attribute_to_scim_attribute_error():
326+
"""Test error case where get_field_root_type returns None."""
327+
from typing import Optional
328+
329+
from pydantic import Field
330+
331+
from scim2_models.base import BaseModel
332+
from scim2_models.rfc7643.resource import model_attribute_to_scim_attribute
333+
334+
# Create a model with a field that has no clear root type
335+
class TestModel(BaseModel):
336+
problematic_field: Optional[str] = Field(default=None)
337+
338+
# Mock get_field_root_type to return None
339+
original_method = TestModel.get_field_root_type
340+
TestModel.get_field_root_type = classmethod(lambda cls, attr: None)
341+
342+
try:
343+
with pytest.raises(
344+
ValueError,
345+
match="Could not determine root type for attribute problematic_field",
346+
):
347+
model_attribute_to_scim_attribute(TestModel, "problematic_field")
348+
finally:
349+
# Restore the original method
350+
TestModel.get_field_root_type = original_method

0 commit comments

Comments
 (0)