ddutils 0.2.3__tar.gz → 0.3.0__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.
- {ddutils-0.2.3 → ddutils-0.3.0}/PKG-INFO +107 -2
- {ddutils-0.2.3 → ddutils-0.3.0}/README.md +106 -1
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/__init__.py +2 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/annotation_helpers.py +1 -1
- ddutils-0.3.0/ddutils/convertors.py +86 -0
- ddutils-0.3.0/ddutils/value_helpers.py +37 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/pyproject.toml +1 -1
- {ddutils-0.2.3 → ddutils-0.3.0}/pyproject.toml.orig +1 -1
- ddutils-0.2.3/ddutils/convertors.py +0 -23
- {ddutils-0.2.3 → ddutils-0.3.0}/LICENSE +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/class_helpers.py +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/datetime_helpers.py +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/function_exceptions_extractor.py +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/function_helpers.py +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/module_getter.py +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/object_getter.py +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/py.typed +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/safe_decorators.py +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/scoped_registry.py +0 -0
- {ddutils-0.2.3 → ddutils-0.3.0}/ddutils/sequence_helpers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ddutils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Domain Driven Utils Library
|
|
5
5
|
Keywords: python,ddutils
|
|
6
6
|
Author: davyddd
|
|
@@ -84,16 +84,23 @@ is_subclass(List[str], dict) # False
|
|
|
84
84
|
|
|
85
85
|
### is_complex_sequence
|
|
86
86
|
|
|
87
|
-
Checks whether a type annotation represents a sequence type, excluding `str`, `bytes`, and `bytearray
|
|
87
|
+
Checks whether a type annotation represents a sequence type, excluding `str`, `bytes`, and `bytearray`
|
|
88
|
+
and their subclasses (a `str`-based `Enum` is not a complex sequence).
|
|
88
89
|
|
|
89
90
|
```python
|
|
91
|
+
from enum import Enum
|
|
90
92
|
from typing import List, Tuple
|
|
91
93
|
from ddutils.annotation_helpers import is_complex_sequence
|
|
92
94
|
|
|
93
95
|
|
|
96
|
+
class Kind(str, Enum):
|
|
97
|
+
A = 'a'
|
|
98
|
+
|
|
99
|
+
|
|
94
100
|
is_complex_sequence(List[int]) # True
|
|
95
101
|
is_complex_sequence(Tuple[str, ...]) # True
|
|
96
102
|
is_complex_sequence(str) # False
|
|
103
|
+
is_complex_sequence(Kind) # False
|
|
97
104
|
is_complex_sequence(dict) # False
|
|
98
105
|
```
|
|
99
106
|
|
|
@@ -330,6 +337,36 @@ delta = timedelta(seconds=5, milliseconds=500)
|
|
|
330
337
|
convert_timedelta_to_milliseconds(delta) # 5500
|
|
331
338
|
```
|
|
332
339
|
|
|
340
|
+
### convert_to_base_value
|
|
341
|
+
|
|
342
|
+
Casts a value to its base value type (see [get_base_value_type](#get_base_value_type)):
|
|
343
|
+
a `str` subclass becomes a plain `str`, a `UUID` subclass a plain `UUID`, a `datetime` subclass a plain `datetime`
|
|
344
|
+
with the same `tzinfo` and `fold`, an `Enum` member becomes its `.value` cast the same way, `None` stays `None`.
|
|
345
|
+
Raises `TypeError` for a value without a base type.
|
|
346
|
+
|
|
347
|
+
Normalize a value once, then dispatch on the result with plain `isinstance` checks: subclasses, `Enum` members
|
|
348
|
+
and third-party types such as a `pendulum.DateTime` all land on the stdlib type.
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
from enum import Enum
|
|
352
|
+
from uuid import UUID
|
|
353
|
+
from ddutils.convertors import convert_to_base_value
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
class Kind(str, Enum):
|
|
357
|
+
A = 'a'
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class ProfileId(UUID):
|
|
361
|
+
pass
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
convert_to_base_value(None) # None
|
|
365
|
+
convert_to_base_value(Kind.A) # 'a' (not 'Kind.A', which `str(Kind.A)` would give)
|
|
366
|
+
convert_to_base_value(ProfileId('12345678-1234-5678-1234-567812345678')) # UUID('1234...'), exact type UUID
|
|
367
|
+
convert_to_base_value(object()) # TypeError
|
|
368
|
+
```
|
|
369
|
+
|
|
333
370
|
## Datetime Helpers
|
|
334
371
|
|
|
335
372
|
Utilities for working with dates and times.
|
|
@@ -491,6 +528,74 @@ AST-based introspection for extracting exceptions from function source code.
|
|
|
491
528
|
Parses a function’s source code to find all `raise` statements and identify the exception types they use.
|
|
492
529
|
Returns an iterator of `ExceptionInfo` objects describing each raised exception.
|
|
493
530
|
|
|
531
|
+
## Value Helpers
|
|
532
|
+
|
|
533
|
+
Runtime counterparts of the annotation helpers: they inspect values, not type hints.
|
|
534
|
+
|
|
535
|
+
### get_base_value_type
|
|
536
|
+
|
|
537
|
+
Returns the base value type a value should be treated as, looking past subclasses. The candidates, in order,
|
|
538
|
+
are `BASE_VALUE_TYPES`: `NoneType`, `bool`, `int`, `float`, `str`, `bytes`, `bytearray`, `datetime`, `date`, `time`,
|
|
539
|
+
`timedelta`, `UUID`, `Decimal` (`bool` is checked before `int`, `datetime` before `date`). An `Enum` member is looked at
|
|
540
|
+
through its `.value`. Raises `TypeError` for any other value, a collection or a mapping included. Useful for APIs that dispatch on the exact type
|
|
541
|
+
(serializers, query builders).
|
|
542
|
+
|
|
543
|
+
```python
|
|
544
|
+
from datetime import datetime
|
|
545
|
+
from enum import Enum
|
|
546
|
+
from uuid import UUID
|
|
547
|
+
from ddutils.value_helpers import get_base_value_type
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
class Kind(str, Enum):
|
|
551
|
+
A = 'a'
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
class ProfileId(UUID):
|
|
555
|
+
pass
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
get_base_value_type(None) # <class 'NoneType'>
|
|
559
|
+
get_base_value_type(True) # <class 'bool'>
|
|
560
|
+
get_base_value_type(Kind.A) # <class 'str'>
|
|
561
|
+
get_base_value_type(ProfileId('12345678-1234-5678-1234-567812345678')) # <class 'uuid.UUID'>
|
|
562
|
+
get_base_value_type(datetime.now()) # <class 'datetime.datetime'>
|
|
563
|
+
get_base_value_type(object()) # TypeError
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### unwrap_enum
|
|
567
|
+
|
|
568
|
+
Returns the `.value` of an `Enum` member and any other value as is. `str(Kind.A)` of a `class Kind(str, Enum)`
|
|
569
|
+
is `'Kind.A'`, so a member has to be unwrapped before it is formatted or cast.
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
from enum import Enum
|
|
573
|
+
from ddutils.value_helpers import unwrap_enum
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
class Kind(str, Enum):
|
|
577
|
+
A = 'a'
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
unwrap_enum(Kind.A) # 'a'
|
|
581
|
+
unwrap_enum('a') # 'a'
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### is_complex_sequence_value
|
|
585
|
+
|
|
586
|
+
Checks whether a value is a sequence other than `str`, `bytes` or `bytearray` (subclasses included).
|
|
587
|
+
Sets and mappings are not sequences. The runtime counterpart of [is_complex_sequence](#is_complex_sequence).
|
|
588
|
+
|
|
589
|
+
```python
|
|
590
|
+
from ddutils.value_helpers import is_complex_sequence_value
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
is_complex_sequence_value([1, 2]) # True
|
|
594
|
+
is_complex_sequence_value((1, 2)) # True
|
|
595
|
+
is_complex_sequence_value('abc') # False
|
|
596
|
+
is_complex_sequence_value({1, 2}) # False
|
|
597
|
+
```
|
|
598
|
+
|
|
494
599
|
## Sequence Helpers
|
|
495
600
|
|
|
496
601
|
Safe operations on sequence types.
|
|
@@ -66,16 +66,23 @@ is_subclass(List[str], dict) # False
|
|
|
66
66
|
|
|
67
67
|
### is_complex_sequence
|
|
68
68
|
|
|
69
|
-
Checks whether a type annotation represents a sequence type, excluding `str`, `bytes`, and `bytearray
|
|
69
|
+
Checks whether a type annotation represents a sequence type, excluding `str`, `bytes`, and `bytearray`
|
|
70
|
+
and their subclasses (a `str`-based `Enum` is not a complex sequence).
|
|
70
71
|
|
|
71
72
|
```python
|
|
73
|
+
from enum import Enum
|
|
72
74
|
from typing import List, Tuple
|
|
73
75
|
from ddutils.annotation_helpers import is_complex_sequence
|
|
74
76
|
|
|
75
77
|
|
|
78
|
+
class Kind(str, Enum):
|
|
79
|
+
A = 'a'
|
|
80
|
+
|
|
81
|
+
|
|
76
82
|
is_complex_sequence(List[int]) # True
|
|
77
83
|
is_complex_sequence(Tuple[str, ...]) # True
|
|
78
84
|
is_complex_sequence(str) # False
|
|
85
|
+
is_complex_sequence(Kind) # False
|
|
79
86
|
is_complex_sequence(dict) # False
|
|
80
87
|
```
|
|
81
88
|
|
|
@@ -312,6 +319,36 @@ delta = timedelta(seconds=5, milliseconds=500)
|
|
|
312
319
|
convert_timedelta_to_milliseconds(delta) # 5500
|
|
313
320
|
```
|
|
314
321
|
|
|
322
|
+
### convert_to_base_value
|
|
323
|
+
|
|
324
|
+
Casts a value to its base value type (see [get_base_value_type](#get_base_value_type)):
|
|
325
|
+
a `str` subclass becomes a plain `str`, a `UUID` subclass a plain `UUID`, a `datetime` subclass a plain `datetime`
|
|
326
|
+
with the same `tzinfo` and `fold`, an `Enum` member becomes its `.value` cast the same way, `None` stays `None`.
|
|
327
|
+
Raises `TypeError` for a value without a base type.
|
|
328
|
+
|
|
329
|
+
Normalize a value once, then dispatch on the result with plain `isinstance` checks: subclasses, `Enum` members
|
|
330
|
+
and third-party types such as a `pendulum.DateTime` all land on the stdlib type.
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
from enum import Enum
|
|
334
|
+
from uuid import UUID
|
|
335
|
+
from ddutils.convertors import convert_to_base_value
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class Kind(str, Enum):
|
|
339
|
+
A = 'a'
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class ProfileId(UUID):
|
|
343
|
+
pass
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
convert_to_base_value(None) # None
|
|
347
|
+
convert_to_base_value(Kind.A) # 'a' (not 'Kind.A', which `str(Kind.A)` would give)
|
|
348
|
+
convert_to_base_value(ProfileId('12345678-1234-5678-1234-567812345678')) # UUID('1234...'), exact type UUID
|
|
349
|
+
convert_to_base_value(object()) # TypeError
|
|
350
|
+
```
|
|
351
|
+
|
|
315
352
|
## Datetime Helpers
|
|
316
353
|
|
|
317
354
|
Utilities for working with dates and times.
|
|
@@ -473,6 +510,74 @@ AST-based introspection for extracting exceptions from function source code.
|
|
|
473
510
|
Parses a function’s source code to find all `raise` statements and identify the exception types they use.
|
|
474
511
|
Returns an iterator of `ExceptionInfo` objects describing each raised exception.
|
|
475
512
|
|
|
513
|
+
## Value Helpers
|
|
514
|
+
|
|
515
|
+
Runtime counterparts of the annotation helpers: they inspect values, not type hints.
|
|
516
|
+
|
|
517
|
+
### get_base_value_type
|
|
518
|
+
|
|
519
|
+
Returns the base value type a value should be treated as, looking past subclasses. The candidates, in order,
|
|
520
|
+
are `BASE_VALUE_TYPES`: `NoneType`, `bool`, `int`, `float`, `str`, `bytes`, `bytearray`, `datetime`, `date`, `time`,
|
|
521
|
+
`timedelta`, `UUID`, `Decimal` (`bool` is checked before `int`, `datetime` before `date`). An `Enum` member is looked at
|
|
522
|
+
through its `.value`. Raises `TypeError` for any other value, a collection or a mapping included. Useful for APIs that dispatch on the exact type
|
|
523
|
+
(serializers, query builders).
|
|
524
|
+
|
|
525
|
+
```python
|
|
526
|
+
from datetime import datetime
|
|
527
|
+
from enum import Enum
|
|
528
|
+
from uuid import UUID
|
|
529
|
+
from ddutils.value_helpers import get_base_value_type
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
class Kind(str, Enum):
|
|
533
|
+
A = 'a'
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
class ProfileId(UUID):
|
|
537
|
+
pass
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
get_base_value_type(None) # <class 'NoneType'>
|
|
541
|
+
get_base_value_type(True) # <class 'bool'>
|
|
542
|
+
get_base_value_type(Kind.A) # <class 'str'>
|
|
543
|
+
get_base_value_type(ProfileId('12345678-1234-5678-1234-567812345678')) # <class 'uuid.UUID'>
|
|
544
|
+
get_base_value_type(datetime.now()) # <class 'datetime.datetime'>
|
|
545
|
+
get_base_value_type(object()) # TypeError
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
### unwrap_enum
|
|
549
|
+
|
|
550
|
+
Returns the `.value` of an `Enum` member and any other value as is. `str(Kind.A)` of a `class Kind(str, Enum)`
|
|
551
|
+
is `'Kind.A'`, so a member has to be unwrapped before it is formatted or cast.
|
|
552
|
+
|
|
553
|
+
```python
|
|
554
|
+
from enum import Enum
|
|
555
|
+
from ddutils.value_helpers import unwrap_enum
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
class Kind(str, Enum):
|
|
559
|
+
A = 'a'
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
unwrap_enum(Kind.A) # 'a'
|
|
563
|
+
unwrap_enum('a') # 'a'
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### is_complex_sequence_value
|
|
567
|
+
|
|
568
|
+
Checks whether a value is a sequence other than `str`, `bytes` or `bytearray` (subclasses included).
|
|
569
|
+
Sets and mappings are not sequences. The runtime counterpart of [is_complex_sequence](#is_complex_sequence).
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
from ddutils.value_helpers import is_complex_sequence_value
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
is_complex_sequence_value([1, 2]) # True
|
|
576
|
+
is_complex_sequence_value((1, 2)) # True
|
|
577
|
+
is_complex_sequence_value('abc') # False
|
|
578
|
+
is_complex_sequence_value({1, 2}) # False
|
|
579
|
+
```
|
|
580
|
+
|
|
476
581
|
## Sequence Helpers
|
|
477
582
|
|
|
478
583
|
Safe operations on sequence types.
|
|
@@ -34,7 +34,7 @@ def is_subclass(annotation: Any, *base_types: Any) -> bool:
|
|
|
34
34
|
|
|
35
35
|
def is_complex_sequence(annotation: Any) -> bool:
|
|
36
36
|
annotation_origin = get_annotation_origin(annotation)
|
|
37
|
-
return
|
|
37
|
+
return not is_subclass(annotation_origin, *NON_COMPLEX_SEQUENCE_TYPES) and is_subclass(annotation_origin, Sequence)
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def get_complex_sequence_element_annotation(annotation: Any) -> Any:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from datetime import date, datetime, time, timedelta
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from types import NoneType
|
|
5
|
+
from typing import Any, Protocol
|
|
6
|
+
from uuid import UUID
|
|
7
|
+
|
|
8
|
+
from ddutils.value_helpers import BASE_VALUE_TYPES, get_base_value_type, unwrap_enum
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ClassInstanceWithDict(Protocol):
|
|
12
|
+
__dict__: dict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def convert_camel_case_to_snake_case(string: str) -> str:
|
|
16
|
+
return ''.join(f'_{char.lower()}' if char.isupper() else char for char in string).lstrip('_')
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def convert_to_repr(obj: ClassInstanceWithDict) -> str:
|
|
20
|
+
class_name = obj.__class__.__name__
|
|
21
|
+
if not hasattr(obj, '__dict__'):
|
|
22
|
+
raise TypeError(f'Instance of {class_name} class does not have a `__dict__` attribute.')
|
|
23
|
+
|
|
24
|
+
attrs = ', '.join(f'{key}={value!r}' for key, value in obj.__dict__.items() if not key.startswith('_'))
|
|
25
|
+
return f'{class_name}({attrs})'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def convert_timedelta_to_milliseconds(delta: timedelta) -> int:
|
|
29
|
+
return int(delta.total_seconds() * 1000)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _convert_to_datetime(value: datetime) -> datetime:
|
|
33
|
+
return datetime(
|
|
34
|
+
value.year, value.month, value.day, value.hour, value.minute, value.second, value.microsecond,
|
|
35
|
+
value.tzinfo, fold=value.fold,
|
|
36
|
+
) # fmt: skip
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _convert_to_date(value: date) -> date:
|
|
40
|
+
return date(value.year, value.month, value.day)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _convert_to_time(value: time) -> time:
|
|
44
|
+
return time(value.hour, value.minute, value.second, value.microsecond, value.tzinfo, fold=value.fold)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _convert_to_timedelta(value: timedelta) -> timedelta:
|
|
48
|
+
# not `total_seconds()`: it goes through a float and loses microseconds on large values
|
|
49
|
+
return timedelta(days=value.days, seconds=value.seconds, microseconds=value.microseconds)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _convert_to_uuid(value: UUID) -> UUID:
|
|
53
|
+
return UUID(int=value.int)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# A cast for every type of `BASE_VALUE_TYPES`: the constructor where it accepts an instance of a subclass,
|
|
57
|
+
# a function building the value from its fields otherwise
|
|
58
|
+
BASE_VALUE_CONVERTORS: dict[type, Callable[[Any], Any]] = {
|
|
59
|
+
NoneType: lambda _: None,
|
|
60
|
+
bool: bool,
|
|
61
|
+
int: int,
|
|
62
|
+
float: float,
|
|
63
|
+
str: str,
|
|
64
|
+
bytes: bytes,
|
|
65
|
+
bytearray: bytearray,
|
|
66
|
+
datetime: _convert_to_datetime,
|
|
67
|
+
date: _convert_to_date,
|
|
68
|
+
time: _convert_to_time,
|
|
69
|
+
timedelta: _convert_to_timedelta,
|
|
70
|
+
UUID: _convert_to_uuid,
|
|
71
|
+
Decimal: Decimal,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if set(BASE_VALUE_CONVERTORS) != set(BASE_VALUE_TYPES):
|
|
75
|
+
raise RuntimeError('`BASE_VALUE_CONVERTORS` must define a convertor for exactly the types of `BASE_VALUE_TYPES`')
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def convert_to_base_value(value: Any) -> Any:
|
|
79
|
+
"""
|
|
80
|
+
Cast a value to its base value type (see `get_base_value_type`): a `str` subclass becomes a plain `str`,
|
|
81
|
+
a `UUID` subclass a plain `UUID`, a `datetime` subclass a plain `datetime` with the same `tzinfo` and `fold`,
|
|
82
|
+
an `Enum` member becomes its `.value` cast the same way, `None` stays `None`.
|
|
83
|
+
Raises `TypeError` for a value without a base type.
|
|
84
|
+
"""
|
|
85
|
+
value = unwrap_enum(value)
|
|
86
|
+
return BASE_VALUE_CONVERTORS[get_base_value_type(value)](value)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from datetime import date, datetime, time, timedelta
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from types import NoneType
|
|
6
|
+
from typing import Any
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
from ddutils.annotation_helpers import NON_COMPLEX_SEQUENCE_TYPES
|
|
10
|
+
|
|
11
|
+
# The order matters, a value is matched against the first type it is an instance of:
|
|
12
|
+
# `bool` is a subclass of `int`, `datetime` is a subclass of `date`
|
|
13
|
+
BASE_VALUE_TYPES = (NoneType, bool, int, float, str, bytes, bytearray, datetime, date, time, timedelta, UUID, Decimal)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def unwrap_enum(value: Any) -> Any:
|
|
17
|
+
"""Return the `.value` of an `Enum` member, any other value as is."""
|
|
18
|
+
return value.value if isinstance(value, Enum) else value
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_base_value_type(value: Any) -> type:
|
|
22
|
+
"""
|
|
23
|
+
Return the base value type a value should be treated as, looking past subclasses: one of `BASE_VALUE_TYPES`
|
|
24
|
+
(`NoneType`, `bool`, `int`, `float`, `str`, `bytes`, `bytearray`, `datetime`, `date`, `time`, `timedelta`, `UUID`,
|
|
25
|
+
`Decimal`). An `Enum` member is looked at through its `.value`. Raises `TypeError` for any other value.
|
|
26
|
+
"""
|
|
27
|
+
value = unwrap_enum(value)
|
|
28
|
+
for base_type in BASE_VALUE_TYPES:
|
|
29
|
+
if isinstance(value, base_type):
|
|
30
|
+
return base_type
|
|
31
|
+
|
|
32
|
+
raise TypeError(f'Value of type `{type(value).__name__}` has no base value type')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def is_complex_sequence_value(value: Any) -> bool:
|
|
36
|
+
"""Return whether a value is a sequence other than `str`, `bytes` or `bytearray`; subclasses are handled."""
|
|
37
|
+
return isinstance(value, Sequence) and not isinstance(value, NON_COMPLEX_SEQUENCE_TYPES)
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
from datetime import timedelta
|
|
2
|
-
from typing import Protocol
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class ClassInstanceWithDict(Protocol):
|
|
6
|
-
__dict__: dict
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def convert_camel_case_to_snake_case(string: str) -> str:
|
|
10
|
-
return ''.join(f'_{char.lower()}' if char.isupper() else char for char in string).lstrip('_')
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def convert_to_repr(obj: ClassInstanceWithDict) -> str:
|
|
14
|
-
class_name = obj.__class__.__name__
|
|
15
|
-
if not hasattr(obj, '__dict__'):
|
|
16
|
-
raise TypeError(f'Instance of {class_name} class does not have a `__dict__` attribute.')
|
|
17
|
-
|
|
18
|
-
attrs = ', '.join(f'{key}={value!r}' for key, value in obj.__dict__.items() if not key.startswith('_'))
|
|
19
|
-
return f'{class_name}({attrs})'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def convert_timedelta_to_milliseconds(delta: timedelta) -> int:
|
|
23
|
-
return int(delta.total_seconds() * 1000)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|