panda-pytest-assertions 0.1.1__tar.gz → 0.2.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 (74) hide show
  1. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/PKG-INFO +1 -1
  2. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_context/__init__.py +2 -1
  3. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_context/_assert_context.py +50 -19
  4. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/__init__.py +6 -0
  5. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_asserter_factories/built_in_asserter_factory.py +4 -0
  6. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_converters/expectation_from_yaml.py +30 -0
  7. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_converters/expectation_to_yaml.py +6 -0
  8. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_expectation_modificators/__init__.py +5 -0
  9. panda_pytest_assertions-0.2.1/panda_pytest_assertions/assert_object/_expectation_modificators/identity.py +14 -0
  10. panda_pytest_assertions-0.2.1/panda_pytest_assertions/assert_object/_expectation_modificators/is_type.py +90 -0
  11. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/__init__.py +6 -0
  12. panda_pytest_assertions-0.2.1/panda_pytest_assertions/assert_object/_objects_asserters/identity_asserter.py +36 -0
  13. panda_pytest_assertions-0.2.1/panda_pytest_assertions/assert_object/_objects_asserters/is_type_asserter.py +51 -0
  14. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/__init__.py +2 -0
  15. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/__init__.py +2 -0
  16. panda_pytest_assertions-0.2.1/panda_pytest_assertions/assert_object/generators/_expectation_definitions/is_type.py +8 -0
  17. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generator_factories/built_in_generator_factory.py +2 -0
  18. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/__init__.py +2 -0
  19. panda_pytest_assertions-0.2.1/panda_pytest_assertions/assert_object/generators/_generators/is_type_generator.py +44 -0
  20. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/pyproject.toml +1 -1
  21. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/LICENSE +0 -0
  22. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/README.md +0 -0
  23. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/__init__.py +0 -0
  24. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_assert_object.py +0 -0
  25. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_asserter_factories/__init__.py +0 -0
  26. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_asserter_factories/asserter_factory.py +0 -0
  27. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_converters/__init__.py +0 -0
  28. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_expectation_modificators/mapping_subset.py +0 -0
  29. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_expectation_modificators/object_attibutes.py +0 -0
  30. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_expectation_modificators/stringified.py +0 -0
  31. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_expectation_modificators/unordered.py +0 -0
  32. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_expectation_modificators/with_type.py +0 -0
  33. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/asserter.py +0 -0
  34. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/equality_asserter.py +0 -0
  35. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/mapping_asserter.py +0 -0
  36. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/mapping_subset_asserter.py +0 -0
  37. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/object_attributes_asserter.py +0 -0
  38. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/ordered_elements_asserter.py +0 -0
  39. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/stringified_asserter.py +0 -0
  40. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/unordered_elements_asserter.py +0 -0
  41. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_objects_asserters/with_type_asserter.py +0 -0
  42. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/_protocols.py +0 -0
  43. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/equality.py +0 -0
  44. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/mapping.py +0 -0
  45. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/mapping_subset.py +0 -0
  46. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/object_attributes.py +0 -0
  47. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/of_type.py +0 -0
  48. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/stringified.py +0 -0
  49. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/uniform_mapping.py +0 -0
  50. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/uniform_mapping_subset.py +0 -0
  51. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/uniform_ordered.py +0 -0
  52. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/uniform_unordered.py +0 -0
  53. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/union.py +0 -0
  54. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_expectation_definitions/with_type.py +0 -0
  55. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generate_expectation.py +0 -0
  56. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generator_factories/__init__.py +0 -0
  57. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generator_factories/generator_factory.py +0 -0
  58. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/equality_generator.py +0 -0
  59. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/equality_key_mapping_generator.py +0 -0
  60. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/exceptions.py +0 -0
  61. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/generator.py +0 -0
  62. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/mapping_generator.py +0 -0
  63. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/mapping_subset_generator.py +0 -0
  64. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/object_attributes_generator.py +0 -0
  65. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/of_type_generator.py +0 -0
  66. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/ordered_generator.py +0 -0
  67. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/stringified_generator.py +0 -0
  68. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/uniform_mapping_generator.py +0 -0
  69. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/uniform_mapping_subset_generator.py +0 -0
  70. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/uniform_ordered_generator.py +0 -0
  71. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/union_generator.py +0 -0
  72. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/unordered_generator.py +0 -0
  73. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/assert_object/generators/_generators/with_type_generator.py +0 -0
  74. {panda_pytest_assertions-0.1.1 → panda_pytest_assertions-0.2.1}/panda_pytest_assertions/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panda-pytest-assertions
3
- Version: 0.1.1
3
+ Version: 0.2.1
4
4
  Summary: Complex assertions that can be used when writing test with pytest
5
5
  Home-page: https://github.com/KRunchPL/panda-pytest-assertions
6
6
  License: Apache-2.0
@@ -6,10 +6,11 @@ pytest.register_assert_rewrite(__name__ + '._assert_context')
6
6
  # isort: on
7
7
 
8
8
 
9
- from ._assert_context import assert_context, AssertContext
9
+ from ._assert_context import assert_context, AssertContext, ExceptionMatch
10
10
 
11
11
 
12
12
  __all__ = [
13
13
  'assert_context',
14
14
  'AssertContext',
15
+ 'ExceptionMatch',
15
16
  ]
@@ -1,6 +1,6 @@
1
1
  from collections.abc import Generator
2
2
  from contextlib import contextmanager, ExitStack, nullcontext
3
- from typing import Any, overload
3
+ from typing import Any, NamedTuple, overload
4
4
 
5
5
  import pytest
6
6
 
@@ -32,18 +32,29 @@ class AssertContext:
32
32
  assert expected_result == self._result
33
33
 
34
34
 
35
+ class ExceptionMatch(NamedTuple):
36
+ #: type of expected exception
37
+ type_: type[BaseException]
38
+ #: match parameter passed to pytest.raises
39
+ match_: str
40
+
41
+
35
42
  @overload
36
43
  @contextmanager
37
44
  def assert_context( # noqa: D418
38
45
  *,
39
- exception: type[BaseException] | _Unset = ...,
46
+ exception: type[BaseException] | ExceptionMatch | None | _Unset = ...,
40
47
  result: Any | _Unset = ..., # noqa: ANN401
41
48
  ) -> Generator[AssertContext, None, None]:
42
49
  """
43
50
  Context manager that makes sure `with` block behaved according to the arguments.
44
51
 
45
- If the `exception` parameter is set, the `with` block is expected to raise an exception of provided type.
46
- If it is NOT set, the `with` block is expected NOT TO raise any exceptions.
52
+ If the `exception` parameter is set to exception type, the `with` block is expected to raise an exception
53
+ of provided type. If the `exception` parameter is set to an instance of `ExceptionMatch` class, the `with`
54
+ block is expected to raise an exception of type specified in `type_` field and the value that matches the
55
+ string specified in `match_` field. For exact matching logic see `match` parameter of the `pytest.raises`.
56
+ If the `exception` parameter is NOT set or is set to `None`, the `with` block is expected NOT TO raise
57
+ any exceptions.
47
58
 
48
59
  If the `result` parameter is set, its value is expected to be equal to `with` block result. The `with`
49
60
  block result is set with the `set` method of an object yielded by the context manager. The comparison
@@ -51,7 +62,7 @@ def assert_context( # noqa: D418
51
62
  If the `result` parameter is NOT set, the `set` method of yielded object is expected
52
63
  not to be called at all.
53
64
 
54
- :param exception: type of exception, expected to be raised
65
+ :param exception: specification of exception, expected to be raised
55
66
  :param result: value expected to be set with `set` method of yielded object
56
67
  """
57
68
 
@@ -60,7 +71,7 @@ def assert_context( # noqa: D418
60
71
  @contextmanager
61
72
  def assert_context( # noqa: D418
62
73
  *,
63
- behaviour: type[BaseException] | Any | _Unset = ..., # noqa: ANN401
74
+ behaviour: type[BaseException] | ExceptionMatch | Any | _Unset = ..., # noqa: ANN401
64
75
  ) -> Generator[AssertContext, None, None]:
65
76
  """
66
77
  Context manager that makes sure `with` block behaved according to the arguments.
@@ -68,9 +79,11 @@ def assert_context( # noqa: D418
68
79
  Context manager yields a single object used that provides `set` method used to set the `with`
69
80
  block result that is to be asserted.
70
81
 
71
- If the `behaviour` parameter is set to an exception type, the `with` block is expected
72
- to raise an exception of provided type and the `set` method of yielded object is expected
73
- not to be called at all.
82
+ If the `behaviour` parameter is set to exception type, the `with` block is expected to raise an exception
83
+ of provided type. If the `behaviour` parameter is set to an instance of `ExceptionMatch` class, the `with`
84
+ block is expected to raise an exception of type specified in `type_` field and the value that matches the
85
+ string specified in `match_` field. For exact matching logic see `match` parameter of the `pytest.raises`.
86
+ In both cases the `set` method of yielded object is expected not to be called at all.
74
87
 
75
88
  If the `behaviour` parameter is set to anything else, its value is expected to be equal to `with`
76
89
  block result. The comparison is used with `==` operator, where `result` argument is on the left side and
@@ -86,20 +99,24 @@ def assert_context( # noqa: D418
86
99
  @contextmanager
87
100
  def assert_context(
88
101
  *,
89
- exception: type[BaseException] | _Unset = _UNSET,
102
+ exception: type[BaseException] | ExceptionMatch | None | _Unset = _UNSET,
90
103
  result: Any | _Unset = _UNSET,
91
- behaviour: type[BaseException] | Any | _Unset = _UNSET,
104
+ behaviour: type[BaseException] | ExceptionMatch | Any | _Unset = _UNSET,
92
105
  ) -> Generator[AssertContext, None, None]:
93
106
  """
94
107
  Context manager that makes sure `with` block behaved according to the arguments.
95
108
 
96
109
  Pair of parameters `exception` and `result` are exclusive with parameter `behaviour`. If `behaviour`
97
- is set to an exception type, it is the same as if `exception` would be set to this value and `result`
98
- would not be set. If `behaviour` is set to any other value, its treated as the value of `result` and the
99
- `exception` remains unset.
110
+ is set to an exception type or `ExceptionMatch` instance, it is the same as if `exception` would be set to
111
+ this value and `result` would not be set. If `behaviour` is set to any other value, its treated as the
112
+ value of `result` and the `exception` remains unset.
100
113
 
101
- If the `exception` parameter is set, the `with` block is expected to raise an exception of provided type.
102
- If it is NOT set, the `with` block is expected NOT TO raise any exceptions.
114
+ If the `exception` parameter is set to exception type, the `with` block is expected to raise an exception
115
+ of provided type. If the `exception` parameter is set to an instance of `ExceptionMatch` class, the `with`
116
+ block is expected to raise an exception of type specified in `type_` field and the value that matches the
117
+ string specified in `match_` field. For exact matching logic see `match` parameter of the `pytest.raises`.
118
+ If the `exception` parameter is NOT set or is set to `None`, the `with` block is expected NOT TO raise
119
+ any exceptions.
103
120
 
104
121
  If the `result` parameter is set, its value is expected to be equal to `with` block result. The `with`
105
122
  block result is set with the `set` method of an object yielded by the context manager. The comparison
@@ -107,7 +124,7 @@ def assert_context(
107
124
  If the `result` parameter is NOT set, the `set` method of yielded object is expected
108
125
  not to be called at all.
109
126
 
110
- :param exception: type of exception, expected to be raised
127
+ :param exception: specification of exception, expected to be raised
111
128
  :param result: value expected to be set with `set` method of yielded object
112
129
  :param behaviour: a common name for `exception` and `result` parameters, exclusive to them
113
130
  :yield: object used to set the result of a `with` block
@@ -115,12 +132,26 @@ def assert_context(
115
132
  if behaviour != _UNSET:
116
133
  assert exception == _UNSET
117
134
  assert result == _UNSET
118
- if isinstance(behaviour, type) and issubclass(behaviour, BaseException):
135
+ if isinstance(behaviour, ExceptionMatch) or (
136
+ isinstance(behaviour, type) and issubclass(behaviour, BaseException)
137
+ ):
119
138
  exception = behaviour
120
139
  else:
121
140
  result = behaviour
141
+ if isinstance(exception, ExceptionMatch):
142
+ match_ = exception.match_
143
+ exception = exception.type_
144
+ else:
145
+ match_ = None
122
146
  context = AssertContext()
123
- exception_behaviour = nullcontext() if isinstance(exception, _Unset) else pytest.raises(exception)
147
+ exception_behaviour = (
148
+ nullcontext()
149
+ if isinstance(exception, _Unset | None)
150
+ else pytest.raises(
151
+ exception,
152
+ match=match_,
153
+ )
154
+ )
124
155
 
125
156
  with ExitStack() as behaviours_stack:
126
157
  behaviours_stack.enter_context(context.assert_result_equal(result))
@@ -7,6 +7,9 @@ from ._converters import (
7
7
  ExpectationYamlLoader,
8
8
  )
9
9
  from ._expectation_modificators import (
10
+ Is,
11
+ is_type,
12
+ IsType,
10
13
  MappingSubset,
11
14
  ObjectAttributes,
12
15
  Stringified,
@@ -27,6 +30,9 @@ __all__ = [
27
30
  'expectation_from_yaml',
28
31
  'ExpectationYamlDumper',
29
32
  'ExpectationYamlLoader',
33
+ 'Is',
34
+ 'is_type',
35
+ 'IsType',
30
36
  'MappingSubset',
31
37
  'ObjectAttributes',
32
38
  'Stringified',
@@ -1,5 +1,7 @@
1
1
  from panda_pytest_assertions.assert_object._objects_asserters import (
2
2
  EqualityAsserter,
3
+ IdentityAsserter,
4
+ IsTypeAsserter,
3
5
  MappingAsserter,
4
6
  MappingSubsetAsserter,
5
7
  ObjectAttributesAsserter,
@@ -20,6 +22,8 @@ class BuiltInAsserterFactory(AsserterFactory):
20
22
 
21
23
  # MUST be the first one as it implements a fallback mechanism and matches everything
22
24
  BuiltInAsserterFactory.register_asserter(EqualityAsserter)
25
+ BuiltInAsserterFactory.register_asserter(IdentityAsserter)
26
+ BuiltInAsserterFactory.register_asserter(IsTypeAsserter)
23
27
  BuiltInAsserterFactory.register_asserter(MappingAsserter)
24
28
  BuiltInAsserterFactory.register_asserter(MappingSubsetAsserter)
25
29
  BuiltInAsserterFactory.register_asserter(ObjectAttributesAsserter)
@@ -5,6 +5,7 @@ import yaml
5
5
  from yaml.constructor import ConstructorError
6
6
 
7
7
  from panda_pytest_assertions.assert_object._expectation_modificators import (
8
+ IsType,
8
9
  MappingSubset,
9
10
  ObjectAttributes,
10
11
  Stringified,
@@ -112,3 +113,32 @@ def _(loader: ExpectationYamlLoader, node: yaml.Node) -> WithType:
112
113
  problem_mark=node.start_mark,
113
114
  )
114
115
  return WithType(node_data['expectation'], expected_type_name, expected_type_module)
116
+
117
+
118
+ _IS_TYPE_KEYS = {'expected_type_name', 'expected_type_module'}
119
+
120
+
121
+ @ExpectationYamlLoader.add_construct('!IsType')
122
+ def _(loader: ExpectationYamlLoader, node: yaml.Node) -> IsType:
123
+ if not isinstance(node, yaml.MappingNode):
124
+ raise ConstructorError(
125
+ problem=f'expected a mapping node, but found {type(node)}',
126
+ problem_mark=node.start_mark,
127
+ )
128
+ node_data = loader.construct_mapping(node)
129
+ if node_data.keys() != _IS_TYPE_KEYS:
130
+ raise ConstructorError(
131
+ problem=f'IsType keys must be {_IS_TYPE_KEYS}',
132
+ problem_mark=node.start_mark,
133
+ )
134
+ if not isinstance(expected_type_name := node_data['expected_type_name'], str):
135
+ raise ConstructorError(
136
+ problem='value of IsType "expected_type_name" must be a string',
137
+ problem_mark=node.start_mark,
138
+ )
139
+ if not isinstance(expected_type_module := node_data['expected_type_module'], str | None):
140
+ raise ConstructorError(
141
+ problem='value of IsType "expected_type_module" must be a string or None',
142
+ problem_mark=node.start_mark,
143
+ )
144
+ return IsType(expected_type_name, expected_type_module)
@@ -5,6 +5,7 @@ from typing import Any, TypeVar
5
5
  import yaml
6
6
 
7
7
  from panda_pytest_assertions.assert_object._expectation_modificators import (
8
+ IsType,
8
9
  MappingSubset,
9
10
  ObjectAttributes,
10
11
  Stringified,
@@ -72,6 +73,11 @@ def _with_type_representer(dumper: ExpectationYamlDumper, data: WithType) -> yam
72
73
  return dumper.represent_mapping('!WithType', data.__dict__)
73
74
 
74
75
 
76
+ @ExpectationYamlDumper.add_repr(IsType)
77
+ def _(dumper: ExpectationYamlDumper, data: IsType) -> yaml.MappingNode:
78
+ return dumper.represent_mapping('!IsType', data.__dict__)
79
+
80
+
75
81
  @ExpectationYamlDumper.add_repr(Enum, is_multi=True)
76
82
  def _(dumper: ExpectationYamlDumper, data: Enum) -> yaml.Node:
77
83
  expectation = with_type(type(data))(Stringified(str(data)))
@@ -1,3 +1,5 @@
1
+ from .identity import Is
2
+ from .is_type import is_type, IsType
1
3
  from .mapping_subset import MappingSubset
2
4
  from .object_attibutes import ObjectAttributes
3
5
  from .stringified import Stringified
@@ -6,6 +8,9 @@ from .with_type import with_type, WithType
6
8
 
7
9
 
8
10
  __all__ = [
11
+ 'Is',
12
+ 'is_type',
13
+ 'IsType',
9
14
  'MappingSubset',
10
15
  'ObjectAttributes',
11
16
  'Stringified',
@@ -0,0 +1,14 @@
1
+ from typing import Any
2
+
3
+
4
+ class Is:
5
+ """
6
+ Wrapper for the expectation that describes object identity (compared with `is` operator).
7
+ """
8
+
9
+ def __init__(self, value: Any, /) -> None: # noqa: ANN401
10
+ #: expected object
11
+ self.value = value
12
+
13
+ def __repr__(self) -> str:
14
+ return f'Is({self.value!r})'
@@ -0,0 +1,90 @@
1
+ from typing import Any, overload
2
+
3
+
4
+ class IsType:
5
+ """
6
+ Wrapper for the expectation that checks object's type.
7
+ """
8
+
9
+ def __init__(
10
+ self,
11
+ expected_type_name: str,
12
+ expected_type_module: str | None,
13
+ /,
14
+ ) -> None:
15
+ #: expected name of the object type
16
+ self.expected_type_name = expected_type_name
17
+ #: expected module of the object type
18
+ self.expected_type_module = expected_type_module
19
+
20
+ def __repr__(self) -> str:
21
+ return (
22
+ f'IsType('
23
+ f'expected_type_name={self.expected_type_name!r}, '
24
+ f'expected_type_module={self.expected_type_module!r}'
25
+ f')'
26
+ )
27
+
28
+
29
+ @overload
30
+ def is_type(value_type: type[Any], /) -> IsType: # noqa: D418
31
+ """
32
+ Create IsType expectation asserting object type.
33
+
34
+ Expected type name and type module will be extracted from the provided type.
35
+
36
+ :param value_type: type to extract expected type name and module from
37
+ """
38
+
39
+
40
+ @overload
41
+ def is_type( # noqa: D418
42
+ value_type_name: str,
43
+ value_type_module: str | None = None,
44
+ /,
45
+ ) -> IsType:
46
+ """
47
+ Create IsType expectation asserting object type.
48
+
49
+ Expected type name will be equal to `value_type_name` and expected module name will be
50
+ equal to `value_type_module` (which can be not set if is not supposed to be asserted).
51
+
52
+ :param value_type_name: name of the expected type
53
+ :param value_type_module: module of the expected type
54
+ """
55
+
56
+
57
+ def is_type(
58
+ value_type: type[Any] | str,
59
+ value_type_module: str | None = None,
60
+ /,
61
+ ) -> IsType:
62
+ """
63
+ Create IsType expectation asserting object type.
64
+
65
+ If `value_type` is a type, expected type name and type module will be extracted from this type.
66
+
67
+ If `value_type` is a string, expected type name will be equal to it and expected module name will be
68
+ equal to provided `value_type_module` (which can be not set if is not supposed to be asserted).
69
+
70
+ :param value_type: expected type or name of the type
71
+ :param value_type_module: expected type's module
72
+ :return: created expectation
73
+ """
74
+ match value_type, value_type_module:
75
+ case str(), str() | None:
76
+ expected_type_name = value_type
77
+ expected_type_module = value_type_module
78
+ case type(), None:
79
+ expected_type_name = value_type.__name__
80
+ expected_type_module = value_type.__module__
81
+ case _:
82
+ msg = (
83
+ f'IsType helper constructor does not accept the following arguments: '
84
+ f'{(value_type, value_type_module)}'
85
+ )
86
+ raise ValueError(msg)
87
+ return IsType(
88
+ expected_type_name,
89
+ expected_type_module,
90
+ )
@@ -3,6 +3,8 @@
3
3
  import pytest
4
4
 
5
5
  pytest.register_assert_rewrite(__name__ + '.equality_asserter')
6
+ pytest.register_assert_rewrite(__name__ + '.identity_asserter')
7
+ pytest.register_assert_rewrite(__name__ + '.is_type_asserter')
6
8
  pytest.register_assert_rewrite(__name__ + '.mapping_asserter')
7
9
  pytest.register_assert_rewrite(__name__ + '.mapping_subset_asserter')
8
10
  pytest.register_assert_rewrite(__name__ + '.object_attributes_asserter')
@@ -14,6 +16,8 @@ pytest.register_assert_rewrite(__name__ + '.with_type_asserter')
14
16
 
15
17
  from .asserter import Asserter
16
18
  from .equality_asserter import EqualityAsserter
19
+ from .identity_asserter import IdentityAsserter
20
+ from .is_type_asserter import IsTypeAsserter
17
21
  from .mapping_asserter import MappingAsserter
18
22
  from .mapping_subset_asserter import MappingSubsetAsserter
19
23
  from .object_attributes_asserter import ObjectAttributesAsserter
@@ -26,6 +30,8 @@ from .with_type_asserter import WithTypeAsserter
26
30
  __all__ = [
27
31
  'Asserter',
28
32
  'EqualityAsserter',
33
+ 'IdentityAsserter',
34
+ 'IsTypeAsserter',
29
35
  'MappingAsserter',
30
36
  'MappingSubsetAsserter',
31
37
  'ObjectAttributesAsserter',
@@ -0,0 +1,36 @@
1
+ from typing import Any
2
+
3
+ from panda_pytest_assertions.assert_object._protocols import AsserterFactoryProtocol
4
+
5
+ from .._expectation_modificators import Is # noqa: TID252
6
+ from .asserter import Asserter
7
+
8
+
9
+ class IdentityAsserter(Asserter):
10
+ """
11
+ Assert that the object is identical to the expectation's value.
12
+ """
13
+
14
+ @classmethod
15
+ def matches(cls, expectation: Any) -> bool: # noqa: ANN401
16
+ """
17
+ Decide whether provided expectation can be handled by this asserter.
18
+
19
+ This asserter can handle an Is expectation.
20
+
21
+ :param expectation: expectation to be tested
22
+ :return: whether expectation can be handled by this asserter
23
+ """
24
+ return isinstance(expectation, Is)
25
+
26
+ def __init__(self, asserter_factory: type[AsserterFactoryProtocol], expectation: Is) -> None:
27
+ super().__init__(asserter_factory, expectation)
28
+ self.expected_object = expectation.value
29
+
30
+ def assert_object(self, object_: Any) -> None: # noqa: ANN401
31
+ """
32
+ Assert if the expectation is fulfilled by an object.
33
+
34
+ :param object_: an object to be tested
35
+ """
36
+ assert self.expected_object is object_
@@ -0,0 +1,51 @@
1
+ from typing import Any
2
+
3
+ from panda_pytest_assertions.assert_object._protocols import AsserterFactoryProtocol
4
+
5
+ from .._expectation_modificators import IsType # noqa: TID252
6
+ from .asserter import Asserter
7
+
8
+
9
+ class IsTypeAsserter(Asserter):
10
+ """
11
+ Assert that the object is of a proper type.
12
+ """
13
+
14
+ @classmethod
15
+ def matches(cls, expectation: Any) -> bool: # noqa: ANN401
16
+ """
17
+ Decide whether provided expectation can be handled by this asserter.
18
+
19
+ This asserter can handle a IsType expectation.
20
+
21
+ :param expectation: expectation to be tested
22
+ :return: whether expectation can be handled by this asserter
23
+ """
24
+ return isinstance(expectation, IsType)
25
+
26
+ def __init__(self, asserter_factory: type[AsserterFactoryProtocol], expectation: IsType) -> None:
27
+ super().__init__(asserter_factory, expectation)
28
+ self.expected_type_name = expectation.expected_type_name
29
+ self.expected_type_module = expectation.expected_type_module
30
+
31
+ def assert_object(self, object_: Any) -> None: # noqa: ANN401
32
+ """
33
+ Assert if the expectation is fulfilled by an object.
34
+
35
+ :param object_: an object to be tested
36
+ """
37
+ if object_.__class__.__name__ != self.expected_type_name:
38
+ msg = (
39
+ f'Object type ("{type(object_)}") has invalid name. '
40
+ f'Expected "{self.expected_type_name}", got "{object_.__class__.__name__}".'
41
+ )
42
+ raise AssertionError(msg)
43
+ if (
44
+ self.expected_type_module is not None
45
+ and object_.__class__.__module__ != self.expected_type_module
46
+ ):
47
+ msg = (
48
+ f'Object type ("{type(object_)}") is from invalid module. '
49
+ f'Expected "{self.expected_type_module}", got "{object_.__class__.__module__}".'
50
+ )
51
+ raise AssertionError(msg)
@@ -1,5 +1,6 @@
1
1
  from ._expectation_definitions import (
2
2
  EqualityDef,
3
+ IsTypeDef,
3
4
  MappingDef,
4
5
  MappingSubsetDef,
5
6
  ObjectAttributesDef,
@@ -21,6 +22,7 @@ from ._generators import Generator
21
22
 
22
23
  __all__ = [
23
24
  'EqualityDef',
25
+ 'IsTypeDef',
24
26
  'MappingDef',
25
27
  'MappingSubsetDef',
26
28
  'ObjectAttributesDef',
@@ -1,4 +1,5 @@
1
1
  from .equality import EqualityDef
2
+ from .is_type import IsTypeDef
2
3
  from .mapping import MappingDef
3
4
  from .mapping_subset import MappingSubsetDef
4
5
  from .object_attributes import ObjectAttributesDef
@@ -14,6 +15,7 @@ from .with_type import with_type_def, WithTypeDef
14
15
 
15
16
  __all__ = [
16
17
  'EqualityDef',
18
+ 'IsTypeDef',
17
19
  'MappingDef',
18
20
  'MappingSubsetDef',
19
21
  'ObjectAttributesDef',
@@ -0,0 +1,8 @@
1
+ class IsTypeDef:
2
+ """
3
+ Defines expectation validating object type.
4
+ """
5
+
6
+ def __init__(self, /, *, include_module: bool = True) -> None:
7
+ #: indicator whether module shall be included in IsType expectation
8
+ self.include_module = include_module
@@ -1,6 +1,7 @@
1
1
  from .._generators import ( # noqa: TID252
2
2
  EqualityGenerator,
3
3
  EqualityKeyMappingGenerator,
4
+ IsTypeGenerator,
4
5
  MappingGenerator,
5
6
  MappingSubsetGenerator,
6
7
  ObjectAttributesGenerator,
@@ -25,6 +26,7 @@ class BuiltInGeneratorFactory(GeneratorFactory):
25
26
 
26
27
  BuiltInGeneratorFactory.register_generator(EqualityGenerator)
27
28
  BuiltInGeneratorFactory.register_generator(EqualityKeyMappingGenerator)
29
+ BuiltInGeneratorFactory.register_generator(IsTypeGenerator)
28
30
  BuiltInGeneratorFactory.register_generator(MappingGenerator)
29
31
  BuiltInGeneratorFactory.register_generator(MappingSubsetGenerator)
30
32
  BuiltInGeneratorFactory.register_generator(ObjectAttributesGenerator)
@@ -1,6 +1,7 @@
1
1
  from .equality_generator import EqualityGenerator
2
2
  from .equality_key_mapping_generator import EqualityKeyMappingGenerator
3
3
  from .generator import Generator
4
+ from .is_type_generator import IsTypeGenerator
4
5
  from .mapping_generator import MappingGenerator
5
6
  from .mapping_subset_generator import MappingSubsetGenerator
6
7
  from .object_attributes_generator import ObjectAttributesGenerator
@@ -17,6 +18,7 @@ from .with_type_generator import WithTypeGenerator
17
18
 
18
19
  __all__ = [
19
20
  'Generator',
21
+ 'IsTypeGenerator',
20
22
  'EqualityGenerator',
21
23
  'EqualityKeyMappingGenerator',
22
24
  'MappingGenerator',
@@ -0,0 +1,44 @@
1
+ from typing import Any
2
+
3
+ from panda_pytest_assertions.assert_object import GeneratorFactoryProtocol, IsType
4
+
5
+ from .._expectation_definitions import IsTypeDef # noqa: TID252
6
+ from .generator import Generator
7
+
8
+
9
+ class IsTypeGenerator(Generator):
10
+ """
11
+ Generates IsType expectation.
12
+ """
13
+
14
+ @classmethod
15
+ def matches(cls, definition: Any) -> bool: # noqa: ANN401
16
+ """
17
+ Decide whether provided definiton can be handled by this generator.
18
+
19
+ This generator can handle IsTypeDef definition.
20
+
21
+ :param definition: definition to be tested
22
+ :return: whether definition can be handled by this generator
23
+ """
24
+ return isinstance(definition, IsTypeDef)
25
+
26
+ def __init__(
27
+ self,
28
+ generator_factory: type[GeneratorFactoryProtocol],
29
+ definition: IsTypeDef,
30
+ ) -> None:
31
+ super().__init__(generator_factory, definition)
32
+ self.include_module = definition.include_module
33
+
34
+ def generate_expectation(self, object_: Any) -> IsType: # noqa: ANN401
35
+ """
36
+ Generate expectation from given object.
37
+
38
+ :param object_: an object to generate expectation from
39
+ :return: generated expectation
40
+ """
41
+ return IsType(
42
+ object_.__class__.__name__,
43
+ object_.__class__.__module__ if self.include_module else None,
44
+ )
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "panda-pytest-assertions"
3
- version = "0.1.1"
3
+ version = "0.2.1"
4
4
  description = "Complex assertions that can be used when writing test with pytest"
5
5
  authors = ["Cyber Panda <krunchfrompoland@gmail.com>"]
6
6
  readme = "README.md"