scim2-models 0.4.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. {scim2_models-0.4.0 → scim2_models-0.4.1}/PKG-INFO +1 -1
  2. {scim2_models-0.4.0 → scim2_models-0.4.1}/doc/changelog.rst +7 -0
  3. {scim2_models-0.4.0 → scim2_models-0.4.1}/pyproject.toml +1 -1
  4. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/messages/patch_op.py +18 -4
  5. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_patch_op_validation.py +35 -0
  6. {scim2_models-0.4.0 → scim2_models-0.4.1}/uv.lock +1 -1
  7. {scim2_models-0.4.0 → scim2_models-0.4.1}/.github/FUNDING.yml +0 -0
  8. {scim2_models-0.4.0 → scim2_models-0.4.1}/.github/workflows/release.yml +0 -0
  9. {scim2_models-0.4.0 → scim2_models-0.4.1}/.github/workflows/tests.yaml +0 -0
  10. {scim2_models-0.4.0 → scim2_models-0.4.1}/.gitignore +0 -0
  11. {scim2_models-0.4.0 → scim2_models-0.4.1}/.pre-commit-config.yaml +0 -0
  12. {scim2_models-0.4.0 → scim2_models-0.4.1}/.readthedocs.yml +0 -0
  13. {scim2_models-0.4.0 → scim2_models-0.4.1}/LICENSE +0 -0
  14. {scim2_models-0.4.0 → scim2_models-0.4.1}/README.md +0 -0
  15. {scim2_models-0.4.0 → scim2_models-0.4.1}/conftest.py +0 -0
  16. {scim2_models-0.4.0 → scim2_models-0.4.1}/doc/__init__.py +0 -0
  17. {scim2_models-0.4.0 → scim2_models-0.4.1}/doc/conf.py +0 -0
  18. {scim2_models-0.4.0 → scim2_models-0.4.1}/doc/contributing.rst +0 -0
  19. {scim2_models-0.4.0 → scim2_models-0.4.1}/doc/index.rst +0 -0
  20. {scim2_models-0.4.0 → scim2_models-0.4.1}/doc/reference.rst +0 -0
  21. {scim2_models-0.4.0 → scim2_models-0.4.1}/doc/tutorial.rst +0 -0
  22. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.1-user-minimal.json +0 -0
  23. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.2-user-full.json +0 -0
  24. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.3-enterprise_user.json +0 -0
  25. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.4-group.json +0 -0
  26. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.5-service_provider_configuration.json +0 -0
  27. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.6-resource_type-group.json +0 -0
  28. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.6-resource_type-user.json +0 -0
  29. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.7.1-schema-enterprise_user.json +0 -0
  30. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.7.1-schema-group.json +0 -0
  31. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.7.1-schema-user.json +0 -0
  32. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.7.2-schema-resource_type.json +0 -0
  33. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.7.2-schema-schema.json +0 -0
  34. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7643-8.7.2-schema-service_provider_configuration.json +0 -0
  35. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.12-error-bad_request.json +0 -0
  36. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.12-error-not_found.json +0 -0
  37. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.14-user-post_request.json +0 -0
  38. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.14-user-post_response.json +0 -0
  39. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.3-user-post_request.json +0 -0
  40. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.3-user-post_response.json +0 -0
  41. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.4.1-user-known-resource.json +0 -0
  42. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.4.2-list_response-partial_attributes.json +0 -0
  43. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.4.3-list_response-post_query.json +0 -0
  44. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.4.3-search_request.json +0 -0
  45. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.1-user-put_request.json +0 -0
  46. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.1-user-put_response.json +0 -0
  47. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.1-patch_op-add_emails.json +0 -0
  48. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.1-patch_op-add_members.json +0 -0
  49. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.2-patch_op-remove_all_members.json +0 -0
  50. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.2-patch_op-remove_and_add_one_member.json +0 -0
  51. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.2-patch_op-remove_multi_complex_value.json +0 -0
  52. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.2-patch_op-remove_one_member.json +0 -0
  53. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.3-patch_op-replace_all_email_values.json +0 -0
  54. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.3-patch_op-replace_all_members.json +0 -0
  55. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.3-patch_op-replace_street_address.json +0 -0
  56. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.5.2.3-patch_op-replace_user_work_address.json +0 -0
  57. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.6-error-not_found.json +0 -0
  58. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.1-bulk_request-circular_conflict.json +0 -0
  59. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.1-list_response-circular_reference.json +0 -0
  60. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.2-bulk_request-enterprise_user.json +0 -0
  61. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.2-bulk_request-temporary_identifier.json +0 -0
  62. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.2-bulk_response-temporary_identifier.json +0 -0
  63. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.3-bulk_request-multiple_operations.json +0 -0
  64. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.3-bulk_response-error_invalid_syntax.json +0 -0
  65. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.3-bulk_response-multiple_errors.json +0 -0
  66. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.3-bulk_response-multiple_operations.json +0 -0
  67. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.3-error-invalid_syntax.json +0 -0
  68. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.7.4-error-payload_too_large.json +0 -0
  69. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-3.9-user-partial_response.json +0 -0
  70. {scim2_models-0.4.0 → scim2_models-0.4.1}/samples/rfc7644-4-list_response-resource_types.json +0 -0
  71. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/__init__.py +0 -0
  72. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/annotations.py +0 -0
  73. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/attributes.py +0 -0
  74. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/base.py +0 -0
  75. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/constants.py +0 -0
  76. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/context.py +0 -0
  77. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/messages/__init__.py +0 -0
  78. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/messages/bulk.py +0 -0
  79. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/messages/error.py +0 -0
  80. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/messages/list_response.py +0 -0
  81. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/messages/message.py +0 -0
  82. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/messages/search_request.py +0 -0
  83. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/py.typed +0 -0
  84. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/reference.py +0 -0
  85. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/resources/__init__.py +0 -0
  86. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/resources/enterprise_user.py +0 -0
  87. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/resources/group.py +0 -0
  88. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/resources/resource.py +0 -0
  89. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/resources/resource_type.py +0 -0
  90. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/resources/schema.py +0 -0
  91. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/resources/service_provider_config.py +0 -0
  92. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/resources/user.py +0 -0
  93. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/scim_object.py +0 -0
  94. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/urn.py +0 -0
  95. {scim2_models-0.4.0 → scim2_models-0.4.1}/scim2_models/utils.py +0 -0
  96. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/__init__.py +0 -0
  97. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/conftest.py +0 -0
  98. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_dynamic_resources.py +0 -0
  99. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_dynamic_schemas.py +0 -0
  100. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_enterprise_user.py +0 -0
  101. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_errors.py +0 -0
  102. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_group.py +0 -0
  103. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_list_response.py +0 -0
  104. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_model_attributes.py +0 -0
  105. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_model_serialization.py +0 -0
  106. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_model_validation.py +0 -0
  107. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_models.py +0 -0
  108. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_patch_op_add.py +0 -0
  109. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_patch_op_extensions.py +0 -0
  110. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_patch_op_remove.py +0 -0
  111. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_patch_op_replace.py +0 -0
  112. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_path_validation.py +0 -0
  113. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_reference.py +0 -0
  114. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_resource_extension.py +0 -0
  115. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_resource_type.py +0 -0
  116. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_schema.py +0 -0
  117. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_search_request.py +0 -0
  118. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_service_provider_configuration.py +0 -0
  119. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_user.py +0 -0
  120. {scim2_models-0.4.0 → scim2_models-0.4.1}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scim2-models
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: SCIM2 models serialization and validation with pydantic
5
5
  Project-URL: documentation, https://scim2-models.readthedocs.io
6
6
  Project-URL: repository, https://github.com/python-scim/scim2-models
@@ -1,6 +1,13 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ [0.4.1] - 2025-07-23
5
+ --------------------
6
+
7
+ Fixed
8
+ ^^^^^
9
+ - Allow ``TypeVar`` as type parameters for :class:`~scim2_models.PatchOp`.
10
+
4
11
  [0.4.0] - 2025-07-23
5
12
  --------------------
6
13
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "scim2-models"
7
- version = "0.4.0"
7
+ version = "0.4.1"
8
8
  description = "SCIM2 models serialization and validation with pydantic"
9
9
  authors = [{name="Yaal Coop", email="contact@yaal.coop"}]
10
10
  license = {file = "LICENSE"}
@@ -143,7 +143,7 @@ class PatchOp(Message, Generic[T]):
143
143
  - Using PatchOp without a type parameter raises TypeError
144
144
  """
145
145
 
146
- def __new__(cls, *args, **kwargs):
146
+ def __new__(cls, *args: Any, **kwargs: Any):
147
147
  """Create new PatchOp instance with type parameter validation.
148
148
 
149
149
  Only handles the case of direct instantiation without type parameter (PatchOp()).
@@ -165,9 +165,23 @@ class PatchOp(Message, Generic[T]):
165
165
  def __class_getitem__(cls, item):
166
166
  """Validate type parameter when creating parameterized type.
167
167
 
168
- Ensures the type parameter is a concrete Resource subclass (not Resource itself).
169
- Rejects invalid types (str, int, etc.) and Union types.
168
+ Ensures the type parameter is a concrete Resource subclass (not Resource itself)
169
+ or a TypeVar bound to Resource. Rejects invalid types (str, int, etc.) and Union types.
170
170
  """
171
+ # Allow TypeVar as type parameter
172
+ if isinstance(item, TypeVar):
173
+ # Check if TypeVar is bound to Resource or its subclass
174
+ if item.__bound__ is not None and (
175
+ item.__bound__ is Resource
176
+ or (isclass(item.__bound__) and issubclass(item.__bound__, Resource))
177
+ ):
178
+ return super().__class_getitem__(item)
179
+ else:
180
+ raise TypeError(
181
+ f"PatchOp TypeVar must be bound to Resource or its subclass, got {item}. "
182
+ "Example: T = TypeVar('T', bound=Resource)"
183
+ )
184
+
171
185
  # Check if type parameter is a concrete Resource subclass (not Resource itself)
172
186
  if item is Resource:
173
187
  raise TypeError(
@@ -176,7 +190,7 @@ class PatchOp(Message, Generic[T]):
176
190
  )
177
191
  if not (isclass(item) and issubclass(item, Resource) and item is not Resource):
178
192
  raise TypeError(
179
- f"PatchOp type parameter must be a concrete Resource subclass, got {item}. "
193
+ f"PatchOp type parameter must be a concrete Resource subclass or TypeVar, got {item}. "
180
194
  "Use PatchOp[User], PatchOp[Group], etc."
181
195
  )
182
196
 
@@ -379,6 +379,41 @@ def test_patch_error_handling_type_mismatch():
379
379
 
380
380
 
381
381
  T = TypeVar("T", bound=Resource)
382
+ UserT = TypeVar("UserT", bound=User)
383
+ UnboundT = TypeVar("UnboundT")
384
+
385
+
386
+ def test_patch_op_with_typevar_bound_to_resource():
387
+ """Test that PatchOp accepts TypeVar bound to Resource."""
388
+ # Should not raise any exception
389
+ patch_type = PatchOp[T]
390
+ assert patch_type is not None
391
+
392
+
393
+ def test_patch_op_with_typevar_bound_to_resource_subclass():
394
+ """Test that PatchOp accepts TypeVar bound to Resource subclass."""
395
+ # Should not raise any exception
396
+ patch_type = PatchOp[UserT]
397
+ assert patch_type is not None
398
+
399
+
400
+ def test_patch_op_with_unbound_typevar():
401
+ """Test that PatchOp rejects unbound TypeVar."""
402
+ with pytest.raises(
403
+ TypeError,
404
+ match="PatchOp TypeVar must be bound to Resource or its subclass, got ~UnboundT",
405
+ ):
406
+ PatchOp[UnboundT]
407
+
408
+
409
+ def test_patch_op_with_typevar_bound_to_non_resource():
410
+ """Test that PatchOp rejects TypeVar bound to non-Resource class."""
411
+ NonResourceT = TypeVar("NonResourceT", bound=str)
412
+ with pytest.raises(
413
+ TypeError,
414
+ match="PatchOp TypeVar must be bound to Resource or its subclass, got ~NonResourceT",
415
+ ):
416
+ PatchOp[NonResourceT]
382
417
 
383
418
 
384
419
  def test_create_parent_object_return_none():
@@ -940,7 +940,7 @@ wheels = [
940
940
 
941
941
  [[package]]
942
942
  name = "scim2-models"
943
- version = "0.4.0"
943
+ version = "0.4.1"
944
944
  source = { editable = "." }
945
945
  dependencies = [
946
946
  { name = "pydantic", extra = ["email"] },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes