apytizer 0.0.1a0__py3-none-any.whl → 0.0.1b1__py3-none-any.whl

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 (76) hide show
  1. apytizer/__init__.py +2 -12
  2. apytizer/adapters/__init__.py +2 -3
  3. apytizer/adapters/transport_adapter.py +91 -0
  4. apytizer/apis/__init__.py +6 -0
  5. apytizer/apis/abstract_api.py +36 -0
  6. apytizer/apis/web_api.py +461 -0
  7. apytizer/connections/__init__.py +6 -0
  8. apytizer/connections/abstract_connection.py +28 -0
  9. apytizer/connections/http_connection.py +431 -0
  10. apytizer/decorators/__init__.py +5 -5
  11. apytizer/decorators/caching.py +60 -9
  12. apytizer/decorators/chunking.py +105 -0
  13. apytizer/decorators/connection.py +55 -20
  14. apytizer/decorators/json_response.py +93 -0
  15. apytizer/decorators/pagination.py +50 -32
  16. apytizer/endpoints/__init__.py +6 -0
  17. apytizer/endpoints/abstract_endpoint.py +38 -0
  18. apytizer/endpoints/web_endpoint.py +519 -0
  19. apytizer/engines/__init__.py +6 -0
  20. apytizer/engines/abstract_engine.py +45 -0
  21. apytizer/engines/http_engine.py +129 -0
  22. apytizer/errors.py +34 -0
  23. apytizer/factories/__init__.py +5 -0
  24. apytizer/factories/abstract_factory.py +17 -0
  25. apytizer/http_methods.py +34 -0
  26. apytizer/managers/__init__.py +12 -0
  27. apytizer/managers/abstract_manager.py +80 -0
  28. apytizer/managers/base_manager.py +116 -0
  29. apytizer/mappers/__init__.py +6 -0
  30. apytizer/mappers/abstract_mapper.py +48 -0
  31. apytizer/mappers/base_mapper.py +78 -0
  32. apytizer/media_types.py +118 -0
  33. apytizer/models/__init__.py +6 -0
  34. apytizer/models/abstract_model.py +119 -0
  35. apytizer/models/base_model.py +85 -0
  36. apytizer/protocols.py +38 -0
  37. apytizer/repositories/__init__.py +6 -0
  38. apytizer/repositories/abstract_repository.py +81 -0
  39. apytizer/repositories/managed_repository.py +92 -0
  40. apytizer/routes/__init__.py +6 -0
  41. apytizer/routes/abstract_route.py +32 -0
  42. apytizer/routes/base_route.py +138 -0
  43. apytizer/sessions/__init__.py +33 -0
  44. apytizer/sessions/abstract_session.py +63 -0
  45. apytizer/sessions/requests_session.py +125 -0
  46. apytizer/states/__init__.py +6 -0
  47. apytizer/states/abstract_state.py +71 -0
  48. apytizer/states/local_state.py +99 -0
  49. apytizer/utils/__init__.py +9 -4
  50. apytizer/utils/caching.py +39 -0
  51. apytizer/utils/dictionaries.py +375 -0
  52. apytizer/utils/errors.py +104 -0
  53. apytizer/utils/iterables.py +91 -0
  54. apytizer/utils/objects.py +145 -0
  55. apytizer/utils/strings.py +69 -0
  56. apytizer/utils/typing.py +29 -0
  57. apytizer-0.0.1b1.dist-info/METADATA +41 -0
  58. apytizer-0.0.1b1.dist-info/RECORD +60 -0
  59. {apytizer-0.0.1a0.dist-info → apytizer-0.0.1b1.dist-info}/WHEEL +1 -2
  60. apytizer/abstracts/__init__.py +0 -8
  61. apytizer/abstracts/api.py +0 -147
  62. apytizer/abstracts/endpoint.py +0 -177
  63. apytizer/abstracts/model.py +0 -50
  64. apytizer/abstracts/session.py +0 -39
  65. apytizer/adapters/transport.py +0 -40
  66. apytizer/base/__init__.py +0 -8
  67. apytizer/base/api.py +0 -510
  68. apytizer/base/endpoint.py +0 -443
  69. apytizer/base/model.py +0 -119
  70. apytizer/decorators/json.py +0 -35
  71. apytizer/utils/generate_key.py +0 -18
  72. apytizer/utils/merge.py +0 -19
  73. apytizer-0.0.1a0.dist-info/METADATA +0 -27
  74. apytizer-0.0.1a0.dist-info/RECORD +0 -25
  75. apytizer-0.0.1a0.dist-info/top_level.txt +0 -1
  76. {apytizer-0.0.1a0.dist-info → apytizer-0.0.1b1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/states/__init__.py
3
+
4
+ # Local Imports
5
+ from .abstract_state import *
6
+ from .local_state import *
@@ -0,0 +1,71 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/states/abstract_state.py
3
+ """Abstract State Class Interface.
4
+
5
+ This module defines an abstract state class which provides an interface
6
+ for subclasses to implement.
7
+
8
+ """
9
+
10
+ # Standard Library Imports
11
+ from __future__ import annotations
12
+ import abc
13
+ from typing import Any
14
+ from typing import Generator
15
+ from typing import Mapping
16
+ from typing import Optional
17
+
18
+ __all__ = ["AbstractState"]
19
+
20
+
21
+ class AbstractState(abc.ABC):
22
+ """Represents an abstract state."""
23
+
24
+ @abc.abstractmethod
25
+ def __contains__(self, key: str) -> bool:
26
+ raise NotImplementedError
27
+
28
+ @abc.abstractmethod
29
+ def __eq__(self, other: object) -> bool:
30
+ raise NotImplementedError
31
+
32
+ @abc.abstractmethod
33
+ def __getitem__(self, key: str) -> Any:
34
+ raise NotImplementedError
35
+
36
+ @abc.abstractmethod
37
+ def __setitem__(self, key: str, value: Any) -> None:
38
+ raise NotImplementedError
39
+
40
+ @abc.abstractmethod
41
+ def __iter__(self) -> Generator[Any, None, None]:
42
+ raise NotImplementedError
43
+
44
+ @abc.abstractmethod
45
+ def get(self, key: str) -> Any:
46
+ """Abstract method for getting an item from state."""
47
+ raise NotImplementedError
48
+
49
+ @abc.abstractmethod
50
+ def items(self) -> Any:
51
+ """Abstract method for getting items from state."""
52
+ raise NotImplementedError
53
+
54
+ @abc.abstractmethod
55
+ def update(
56
+ self,
57
+ __m: Optional[Mapping[str, Any]] = None,
58
+ **kwargs: Any,
59
+ ) -> None:
60
+ """Abstract method for updating state."""
61
+ raise NotImplementedError
62
+
63
+ @abc.abstractmethod
64
+ def rollback(self) -> None:
65
+ """Abstract method for rolling back changes to state."""
66
+ raise NotImplementedError
67
+
68
+ @abc.abstractmethod
69
+ def save(self) -> None:
70
+ """Abstract method for saving changes to state."""
71
+ raise NotImplementedError
@@ -0,0 +1,99 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/states/base_state.py
3
+ """Base State Class.
4
+
5
+ This module defines the implementation of a base state class.
6
+
7
+ """
8
+
9
+ # Standard Library Imports
10
+ from __future__ import annotations
11
+ import collections
12
+ from typing import Any
13
+ from typing import Dict
14
+ from typing import Generator
15
+ from typing import Mapping
16
+ from typing import Optional
17
+ from typing import Tuple
18
+
19
+ # Local Imports
20
+ from . import AbstractState
21
+ from .. import utils
22
+
23
+ __all__ = ["LocalState"]
24
+
25
+
26
+ class LocalState(AbstractState):
27
+ """Class implements a local state."""
28
+
29
+ def __init__(
30
+ self,
31
+ base: Optional[Dict[str, Any]] = None,
32
+ default: Optional[Dict[str, Any]] = None,
33
+ ) -> None:
34
+ self._state = collections.ChainMap(base or {}, default or {})
35
+
36
+ def __contains__(self, key: str) -> bool:
37
+ return key in self._state
38
+
39
+ def __eq__(self, other: object) -> bool:
40
+ return (
41
+ dict(other) == dict(self)
42
+ if isinstance(other, AbstractState)
43
+ else False
44
+ )
45
+
46
+ def __getitem__(self, key: str) -> Any:
47
+ result = self.get(key)
48
+ return result
49
+
50
+ def __setitem__(self, key: str, value: Any) -> None:
51
+ self._state = utils.deep_set(self._state, key, value)
52
+
53
+ def __iter__(self) -> Generator[Tuple[str, Any], None, None]:
54
+ yield from self._state.items()
55
+
56
+ def get(self, key: str) -> Any:
57
+ """Get an item from state.
58
+
59
+ Args:
60
+ key: Key.
61
+
62
+ Returns:
63
+ Value of key in state.
64
+
65
+ """
66
+ if not isinstance(key, str): # type: ignore
67
+ message = f"expected type 'str', got {type(key)} instead"
68
+ raise TypeError(message)
69
+
70
+ result = utils.deep_get(self._state, key)
71
+ return result
72
+
73
+ def items(self) -> Any:
74
+ """Get items from state."""
75
+ results = self._state.items()
76
+ return results
77
+
78
+ def update(
79
+ self,
80
+ __m: Optional[Mapping[str, Any]] = None,
81
+ **kwargs: Any,
82
+ ) -> None:
83
+ """Update state.
84
+
85
+ Args:
86
+ __m (optional): Mapping. Default ``None``.
87
+ **kwargs: Keyword arguments.
88
+
89
+ """
90
+ self._state.update(__m or {}, **kwargs)
91
+
92
+ def rollback(self) -> None:
93
+ """Roll back changes to state."""
94
+ self._state.clear()
95
+
96
+ def save(self) -> None:
97
+ """Save changes to state."""
98
+ if self._state.maps[0]: # type: ignore
99
+ self._state = self._state.new_child() # type: ignore
@@ -1,6 +1,11 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # src/apytizer/utils/__init__.py
2
3
 
3
- # pylint: skip-file
4
-
5
- from .generate_key import generate_key
6
- from .merge import merge
4
+ # Local Imports
5
+ from .caching import *
6
+ from .dictionaries import *
7
+ from .errors import *
8
+ from .iterables import *
9
+ from .objects import *
10
+ from .strings import *
11
+ from .typing import *
@@ -0,0 +1,39 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Standard Library Import
4
+ from typing import Any
5
+ from typing import Callable
6
+ from typing import Hashable
7
+ from typing import Tuple
8
+
9
+ # Third-Party Imports
10
+ from cachetools.keys import hashkey
11
+
12
+ __all__ = ["generate_key"]
13
+
14
+
15
+ def generate_key(*tags: str) -> Callable[..., Tuple[Hashable, ...]]:
16
+ """Generates a hashable key for caching values.
17
+
18
+ Args:
19
+ *tags: Tags.
20
+
21
+ """
22
+
23
+ def hash_parameters(*args: Any, **kwargs: Any) -> Tuple[Hashable, ...]:
24
+ """Hashes function parameters.
25
+
26
+ Args:
27
+ *args: Positional arguments.
28
+ **kwargs: Keyword arguments.
29
+
30
+ Return:
31
+ Cache Key.
32
+
33
+ """
34
+ result = hashkey(
35
+ *tags, *args, *[f"{k!s}={v!s}" for k, v in sorted(kwargs.items())]
36
+ )
37
+ return result
38
+
39
+ return hash_parameters
@@ -0,0 +1,375 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/utils/dictionaries.py
3
+
4
+ # Standard Library Imports
5
+ from collections import ChainMap
6
+ import functools
7
+ from typing import Any
8
+ from typing import Collection
9
+ from typing import Dict
10
+ from typing import Hashable
11
+ from typing import List
12
+ from typing import Optional
13
+ from typing import Set
14
+ from typing import TypeVar
15
+ from typing import Union
16
+
17
+ # Local Imports
18
+ from .errors import raise_for_instance
19
+ from .typing import allinstance
20
+
21
+ __all__ = [
22
+ "deep_get",
23
+ "deep_set",
24
+ "iter_get",
25
+ "iter_set",
26
+ "omit",
27
+ "pick",
28
+ "merge",
29
+ "remap_keys",
30
+ "remove_nulls",
31
+ ]
32
+
33
+ # Custom types:
34
+ T = TypeVar("T")
35
+
36
+
37
+ def deep_get(
38
+ __d: Dict[Hashable, Any],
39
+ /,
40
+ keys: str,
41
+ default: Optional[object] = None,
42
+ ) -> Any:
43
+ """Get value from nested dictionary object.
44
+
45
+ Args:
46
+ __d: Dictionary object.
47
+ keys: String of keys seperated by periods.
48
+ default (optional): Default if value not found. Default ``None``.
49
+
50
+ Returns:
51
+ Value of key in nested dictionary object.
52
+
53
+ Raises:
54
+ TypeError: when argument is not an instance of 'dict'.
55
+
56
+ """
57
+
58
+ def _get(data: Dict[str, Any], key: str) -> Any:
59
+ """Get value of key from dictionary.
60
+
61
+ Args:
62
+ data: Dictionary object.
63
+ key: Key for which to get value.
64
+
65
+ Returns:
66
+ Value.
67
+
68
+ """
69
+ try:
70
+ result = data.get(key, default)
71
+ except AttributeError: # if data is `None`
72
+ return default
73
+ else:
74
+ return result
75
+
76
+ raise_for_instance(__d, (dict, ChainMap))
77
+ raise_for_instance(keys, str)
78
+
79
+ value = functools.reduce(_get, keys.split("."), __d)
80
+ return value
81
+
82
+
83
+ def deep_set(
84
+ __d: Dict[Hashable, Any],
85
+ /,
86
+ keys: Union[List[str], str],
87
+ value: Any,
88
+ ) -> Dict[Hashable, Any]:
89
+ """Sets key to value in nested dictionary object.
90
+
91
+ Args:
92
+ __d: Dictionary object.
93
+ keys: Either list of keys, or string of keys seperated by periods.
94
+ value: Value to set for key.
95
+
96
+ Returns:
97
+ Updated dictionary object.
98
+
99
+ Raises:
100
+ TypeError: when argument is not an instance of 'dict'.
101
+
102
+ """
103
+ raise_for_instance(__d, (dict, ChainMap))
104
+
105
+ if isinstance(keys, str):
106
+ keys = keys.split(".")
107
+
108
+ try:
109
+ key, remaining = keys[0], keys[1:]
110
+ __d[key] = (
111
+ deep_set(__d.get(key) or {}, remaining, value)
112
+ if len(remaining) >= 1
113
+ else value
114
+ )
115
+ except KeyError as error:
116
+ raise KeyError(f"{key}.{error.args[0]}") from error # type: ignore
117
+
118
+ except (IndexError, TypeError) as error:
119
+ raise KeyError(keys[0]) from error
120
+
121
+ return __d
122
+
123
+
124
+ def iter_get(
125
+ __iter: List[Dict[Hashable, Any]],
126
+ /,
127
+ key: str,
128
+ ) -> List[object]:
129
+ """Get value for key from each dictionary in an iterable object.
130
+
131
+ Args:
132
+ __iter: Iterable object containing dictionaries.
133
+ key: Key for which to retrieve value.
134
+
135
+ Raises:
136
+ TypeError: when argument is not an iterable object.
137
+ ValueError: when not all items are dictionaries.
138
+
139
+ """
140
+ raise_for_instance(__iter, list)
141
+
142
+ if not allinstance(__iter, dict):
143
+ raise ValueError("iterable object must contain dictionaries")
144
+
145
+ results = [deep_get(item, key) for item in __iter]
146
+ return results
147
+
148
+
149
+ def iter_set(
150
+ __iter: List[Dict[Hashable, Any]],
151
+ /,
152
+ key: str,
153
+ value: Any,
154
+ ) -> List[Dict[Hashable, Any]]:
155
+ """Set value of key on each dictionary in an iterable object.
156
+
157
+ Args:
158
+ __iter: Iterable object containing dictionaries.
159
+ key: Key for which to set value.
160
+ value: Value to set.
161
+
162
+ Returns:
163
+ List of updated dictionary objects.
164
+
165
+ Raises:
166
+ TypeError: when argument is not an iterable object.
167
+ ValueError: when not all items are dictionaries.
168
+
169
+ """
170
+ raise_for_instance(__iter, list)
171
+
172
+ if not allinstance(__iter, dict):
173
+ raise ValueError("iterable object must contain dictionaries")
174
+
175
+ results = [deep_set(item, key, value) for item in __iter]
176
+ return results
177
+
178
+
179
+ def merge(
180
+ *args: Optional[Dict[Hashable, T]],
181
+ overwrite: bool = True,
182
+ ) -> Optional[Dict[Hashable, T]]:
183
+ """Combines dictionary objects into a single dictionary.
184
+
185
+ Args:
186
+ *args: Dictionary objects to merge.
187
+ overwrite (optional): Overwrite existing keys. Default ``True``.
188
+
189
+ Returns:
190
+ Merged dictionary.
191
+
192
+ Raises:
193
+ TypeError: when arguments are not all dictionaries.
194
+ ValueError: when keys conflict and overwrite is ``False``.
195
+
196
+ """
197
+ if not allinstance(args, (dict, type(None))):
198
+ raise TypeError("all arguments must be instances of 'dict'")
199
+
200
+ def _merge_dictionaries(
201
+ first: Dict[Hashable, Any],
202
+ second: Dict[Hashable, Any],
203
+ /,
204
+ path: Optional[List[str]] = None,
205
+ overwrite: bool = False,
206
+ ) -> Dict[Hashable, Any]:
207
+ """Merge two dictionaries.
208
+
209
+ Args:
210
+ first: First dictionary.
211
+ second: Second dictionary.
212
+ path: Path of keys in nested dictionary.
213
+ overwrite (optional): Overwrite existing keys. Default ``False``.
214
+
215
+ Raises:
216
+ ValueError: when keys conflict and overwrite is ``False``.
217
+
218
+ .. _Based On:
219
+ https://stackoverflow.com/questions/7204805/how-to-merge-dictionaries-of-dictionaries.
220
+
221
+ """
222
+ __path: List[str] = [] if path is None else path
223
+
224
+ for key in second:
225
+ if key in first:
226
+ if allinstance((first[key], second[key]), dict):
227
+ first[key] = _merge_dictionaries(
228
+ first[key],
229
+ second[key],
230
+ path=[*__path, str(key)],
231
+ overwrite=overwrite,
232
+ )
233
+
234
+ elif allinstance((first[key], second[key]), list):
235
+ first[key] = _merge_lists(first[key], second[key])
236
+
237
+ elif allinstance((first[key], second[key]), set):
238
+ first[key] = _merge_sets(first[key], second[key])
239
+
240
+ elif overwrite is True:
241
+ first[key] = second[key]
242
+
243
+ else:
244
+ location = ".".join(k for k in [*__path, str(key)] if k)
245
+ message = f"Conflict at {location!s}"
246
+ raise ValueError(message)
247
+
248
+ else:
249
+ first[key] = second[key]
250
+
251
+ return first
252
+
253
+ def _merge_lists(first: List[Any], second: List[Any]) -> List[Any]:
254
+ return [*first, *second]
255
+
256
+ def _merge_sets(first: Set[Any], second: Set[Any]) -> Set[Any]:
257
+ return first.union(second)
258
+
259
+ func = functools.partial(_merge_dictionaries, overwrite=overwrite)
260
+ result: Dict[Hashable, Any] = functools.reduce(
261
+ lambda acc, cur: func(acc, cur) if cur else acc, args, {}
262
+ )
263
+ return result if result else None
264
+
265
+
266
+ def omit(
267
+ __d: Dict[Hashable, Any],
268
+ /,
269
+ keys: Collection[str],
270
+ ) -> Dict[Hashable, Any]:
271
+ """Omit multiple key-value pairs from dictionary.
272
+
273
+ Args:
274
+ __d: Dictionary object.
275
+ keys: Collection of keys.
276
+
277
+ Returns:
278
+ Dictionary without the selected key-value pairs.
279
+
280
+ Raises:
281
+ TypeError: when argument is not an instance of 'dict'.
282
+
283
+ """
284
+ raise_for_instance(__d, dict)
285
+
286
+ # TODO: Add support for omitting key-value pairs from nested dictionaries.
287
+ results: Dict[Hashable, Any] = {k: __d[k] for k in __d if k not in keys}
288
+ return results
289
+
290
+
291
+ def pick(
292
+ __d: Dict[Hashable, Any],
293
+ /,
294
+ keys: Collection[str],
295
+ ) -> Dict[Hashable, Any]:
296
+ """Pick multiple values from a dictionary.
297
+
298
+ Args:
299
+ __d: Dictionary object.
300
+ keys: Collection of keys.
301
+
302
+ Returns:
303
+ Dictionary containing the selected key-value pairs.
304
+
305
+ Raises:
306
+ TypeError: when argument is not an instance of 'dict'.
307
+
308
+ """
309
+ raise_for_instance(__d, (dict, ChainMap))
310
+
311
+ def _last(key: str) -> Hashable:
312
+ return key.split(".")[-1]
313
+
314
+ results = {_last(key): deep_get(__d, key) for key in keys}
315
+ return results
316
+
317
+
318
+ def remap_keys(
319
+ __d: Dict[Hashable, Any],
320
+ /,
321
+ key_map: Dict[Hashable, str],
322
+ remove: bool = False,
323
+ ) -> Dict[Hashable, Any]:
324
+ """Remap dictionary object to new keys.
325
+
326
+ Args:
327
+ __d: Dictionary object for which keys will be remapped.
328
+ key_map: Dictionary mapping old keys to new ones.
329
+ remove (optional): Whether to drop key-value pairs if key is not found
330
+ in key map. Default ``False``.
331
+
332
+ Returns:
333
+ Remapped dictionary.
334
+
335
+ Raises:
336
+ TypeError: when argument is not an instance of 'dict'.
337
+
338
+ """
339
+ raise_for_instance(__d, dict)
340
+
341
+ result: Dict[Hashable, Any] = {
342
+ key_map.get(key, key): value
343
+ for key, value in __d.items()
344
+ if key in key_map or remove is False
345
+ }
346
+ return result
347
+
348
+
349
+ def remove_nulls(
350
+ __d: Dict[Hashable, Any],
351
+ /,
352
+ null_values: Optional[Collection[Any]] = None,
353
+ ) -> Dict[Hashable, Any]:
354
+ """Remove all null values from dictionary.
355
+
356
+ Args:
357
+ __d: Dictionary from which to remove null values.
358
+ null_values (optional): Additional values to recognize as null.
359
+
360
+ Returns:
361
+ Dictionary without null values.
362
+
363
+ Raises:
364
+ TypeError: when argument is not an instance of 'dict'.
365
+
366
+ """
367
+ raise_for_instance(__d, dict)
368
+
369
+ nulls = null_values or []
370
+ result: Dict[Hashable, Any] = {
371
+ key: value
372
+ for key, value in __d.items()
373
+ if value is not None and value not in nulls
374
+ }
375
+ return result
@@ -0,0 +1,104 @@
1
+ # -*- coding: utf-8 -*-
2
+ # src/apytizer/utils/errors.py
3
+
4
+ # Standard Library Imports
5
+ from typing import Any
6
+ from typing import Tuple
7
+ from typing import Union
8
+
9
+ # Local Imports
10
+ from .. import utils
11
+
12
+ __all__ = [
13
+ "raise_for_attribute",
14
+ "raise_for_instance",
15
+ "raise_for_none",
16
+ ]
17
+
18
+
19
+ def raise_for_attribute(__obj: object, __attr: str, /) -> None:
20
+ """Raise error if object does not contain expected attribute.
21
+
22
+ Args:
23
+ __obj: Object to check for attribute.
24
+ __attr: Attribute for which to check.
25
+
26
+ """
27
+ if not hasattr(__obj, __attr):
28
+ cls = __obj.__class__.__name__
29
+ message = f"type object '{cls!s}' has no attribute '{__attr!s}'"
30
+ raise AttributeError(message)
31
+
32
+
33
+ def raise_for_instance(
34
+ __value: object,
35
+ __expected: Union[type, Tuple[Union[type, Tuple[Any, ...]], ...]],
36
+ /,
37
+ ) -> None:
38
+ """Raise error if value is not an instance of expected type.
39
+
40
+ Args:
41
+ __value: Object to check for type.
42
+ __expected: Expected type(s).
43
+
44
+ """
45
+ correct_type = isinstance(__value, __expected)
46
+
47
+ if not correct_type and isinstance(__expected, tuple):
48
+ _raise_for_multiple_types(__value, __expected)
49
+
50
+ if not correct_type and not isinstance(__expected, tuple):
51
+ _raise_for_single_type(__value, __expected)
52
+
53
+
54
+ def _raise_for_multiple_types(
55
+ __value: object, __types: Tuple[Union[type, Tuple[Any, ...]], ...], /
56
+ ) -> None:
57
+ """Raise error if value is not among expected types.
58
+
59
+ Args:
60
+ __value: Object to check for type.
61
+ __types: Expected types.
62
+
63
+ """
64
+ type_names = utils.iter_getattr(__types, "__name__")
65
+ formatted_names = utils.iter_format(type_names, "'{}'")
66
+ expected = utils.syntactic_list(formatted_names, "or")
67
+ actual = type(__value).__name__
68
+
69
+ message = f"expected types {expected!s}, got {actual!s} instead"
70
+ raise TypeError(message)
71
+
72
+
73
+ def _raise_for_single_type(__value: object, __type: type, /) -> None:
74
+ """Raise error if value is not an instance of expected type.
75
+
76
+ Args:
77
+ __value: Object to check for type.
78
+ __type: Expected type.
79
+
80
+ """
81
+ expected, actual = f"'{__type.__name__}'", type(__value).__name__
82
+ message = f"expected type {expected!s}, got {actual!s} instead"
83
+ raise TypeError(message)
84
+
85
+
86
+ def raise_for_none(*args: Any, **kwargs: Any) -> None:
87
+ """Raise error if value is None.
88
+
89
+ Args:
90
+ *args: Positional arguments.
91
+ **kwargs: Keyword arguments.
92
+
93
+ Raises:
94
+ ValueError: when any argument is ``None``.
95
+
96
+ """
97
+ if any(arg is None for arg in args):
98
+ message = "argument cannot be 'None'"
99
+ raise ValueError(message)
100
+
101
+ for name, value in kwargs.items():
102
+ if value is None:
103
+ message = f"{name} cannot be 'None'"
104
+ raise ValueError(message)