Skip to content

Commit 0d523a9

Browse files
committed
Standardise type hints and simplify overloads
- Replace custom type aliases from the `toolbox_python.collection_types` module with standard library types like `list[str]`, `Collection[Any]`, and `Dict[Any, Any]`. - Simplify `is_value_of_type()` and `assert_value_of_type()` function overloads by using `Collection[type]` to handle multiple input sequences. - Relocate the `log_levels` type literal to `output.py` module to better align with its usage in output formatting. - Add explicit `Callable` type hints to function aliases within the `checkers.py` module for improved type clarity. - Update the `Defaults()` class docstring to include a dedicated methods section documenting the `.get()` method. - Refactor the `flatten()` function and the `list_columns()` function to use standard collection types and remove redundant `@overload` definitions. - Improve type checking logic in the `checkers.py` module to handle generic collections of types more robustly.
1 parent 13637f7 commit 0d523a9

10 files changed

Lines changed: 151 additions & 238 deletions

File tree

src/toolbox_python/bools.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,12 @@
3333
# ---------------------------------------------------------------------------- #
3434

3535

36-
# ---------------------------------------------------------------------------- #
37-
# Imports ####
38-
# ---------------------------------------------------------------------------- #
39-
40-
41-
# ## Local First Party Imports ----
42-
from toolbox_python.collection_types import str_list
43-
44-
4536
# ---------------------------------------------------------------------------- #
4637
# Exports ####
4738
# ---------------------------------------------------------------------------- #
4839

4940

50-
__all__: str_list = ["strtobool", "STR_TO_BOOL_MAP"]
41+
__all__: list[str] = ["strtobool", "STR_TO_BOOL_MAP"]
5142

5243

5344
# ---------------------------------------------------------------------------- #

src/toolbox_python/checkers.py

Lines changed: 81 additions & 136 deletions
Large diffs are not rendered by default.

src/toolbox_python/collection_types.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
# ## Python StdLib Imports ----
2222
from datetime import datetime
23-
from typing import Any, Literal, Union
23+
from typing import Any, Union
2424

2525

2626
## --------------------------------------------------------------------------- #
@@ -40,7 +40,6 @@
4040
"int_list",
4141
"int_tuple",
4242
"iterable",
43-
"log_levels",
4443
"scalar",
4544
"str_collection",
4645
"str_dict",
@@ -128,15 +127,3 @@
128127
]
129128
```
130129
"""
131-
132-
133-
log_levels = Literal["debug", "info", "warning", "error", "critical"]
134-
"""
135-
!!! note "Summary"
136-
To streamline other functions, this `type` alias is created for all of the `log` levels available.
137-
!!! abstract "Details"
138-
The structure of the `type` is as follows:
139-
```pycon {.py .python linenums="1" title="Type structure"}
140-
Literal["debug", "info", "warning", "error", "critical"]
141-
```
142-
"""

src/toolbox_python/defaults.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,14 @@
4848
# ## Local First Party Imports ----
4949
from toolbox_python.bools import strtobool
5050
from toolbox_python.checkers import is_type
51-
from toolbox_python.collection_types import str_list
5251

5352

5453
# ---------------------------------------------------------------------------- #
5554
# Exports ####
5655
# ---------------------------------------------------------------------------- #
5756

5857

59-
__all__: str_list = ["defaults", "Defaults"]
58+
__all__: list[str] = ["defaults", "Defaults"]
6059

6160

6261
# ---------------------------------------------------------------------------- #
@@ -77,6 +76,11 @@ class Defaults:
7776
When we create and use Python variables, it is sometimes handy to add a default value for that variable.
7877
This class will handle that process.
7978
79+
Methods:
80+
- get(): From the value that is parsed in to the `value` parameter, convert it to `default` if `value` is `#!py None`, and convert it to `cast` if `cast` is not `#!py None`.
81+
- _validate_value_and_default(): Validate to ensure that `value` and `default` are not both `#!py None`.
82+
- _validate_type(): Check to ensure that `check_type` is a valid Python type
83+
8084
???+ example "Examples"
8185
8286
```pycon {.py .python linenums="1" title="Set up data for examples"}
@@ -396,7 +400,7 @@ def _validate_type(
396400
??? tip "See Also"
397401
- [`Defaults.get()`][toolbox_python.defaults.Defaults.get]
398402
"""
399-
valid_types: str_list = [
403+
valid_types: list[str] = [
400404
"bool",
401405
"dict",
402406
"int",

src/toolbox_python/dictionaries.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,13 @@
4444
# ## Python Third Party Imports ----
4545
from typeguard import typechecked
4646

47-
# ## Local First Party Imports ----
48-
from toolbox_python.collection_types import dict_any, dict_str_any, str_list
49-
5047

5148
# ---------------------------------------------------------------------------- #
5249
# Exports ####
5350
# ---------------------------------------------------------------------------- #
5451

55-
__all__: str_list = ["dict_reverse_keys_and_values", "DotDict"]
52+
53+
__all__: list[str] = ["dict_reverse_keys_and_values", "DotDict"]
5654

5755

5856
# ---------------------------------------------------------------------------- #
@@ -63,7 +61,7 @@
6361

6462

6563
@typechecked
66-
def dict_reverse_keys_and_values(dictionary: dict_any) -> dict_str_any:
64+
def dict_reverse_keys_and_values(dictionary: dict[Any, Any]) -> dict[str, Any]:
6765
"""
6866
!!! note "Summary"
6967
Take the `key` and `values` of a dictionary, and reverse them.
@@ -72,7 +70,7 @@ def dict_reverse_keys_and_values(dictionary: dict_any) -> dict_str_any:
7270
This process is simple enough if the `values` are atomic types, like `#!py str`, `#!py int`, or `#!py float` types. But it is a little more tricky when the `values` are more complex types, like `#!py list` or `#!py dict`; here we need to use some recursion.
7371
7472
Params:
75-
dictionary (dict_any):
73+
dictionary (Dict[Any, Any]):
7674
The input `#!py dict` that you'd like to have the `keys` and `values` switched.
7775
7876
Raises:
@@ -82,7 +80,7 @@ def dict_reverse_keys_and_values(dictionary: dict_any) -> dict_str_any:
8280
When there are duplicate `values` being coerced to `keys` in the new dictionary. Raised because a Python `#!py dict` cannot have duplicate keys of the same value.
8381
8482
Returns:
85-
output_dict (dict_str_int):
83+
output_dict (Dict[str,Any]):
8684
The updated `#!py dict`.
8785
8886
???+ example "Examples"
@@ -204,7 +202,7 @@ def dict_reverse_keys_and_values(dictionary: dict_any) -> dict_str_any:
204202
!!! observation "Here, the process would be to run a recursive process when it recognises that any `value` is a `#!py dict` object. So long as there are no duplicate values in any of the contained `#!py dict`'s, the resulting output will be a big, flat dictionary."
205203
</div>
206204
"""
207-
output_dict: dict_str_any = dict()
205+
output_dict: dict[str, Any] = dict()
208206
for key, value in dictionary.items():
209207
if isinstance(value, (str, int, float)):
210208
output_dict[str(value)] = key
@@ -219,7 +217,7 @@ def dict_reverse_keys_and_values(dictionary: dict_any) -> dict_str_any:
219217
)
220218
output_dict[str(elem)] = key
221219
elif isinstance(value, dict):
222-
interim_dict: dict_str_any = dict_reverse_keys_and_values(value)
220+
interim_dict: dict[str, Any] = dict_reverse_keys_and_values(value)
223221
output_dict = {
224222
**output_dict,
225223
**interim_dict,

src/toolbox_python/generators.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,14 @@
4141

4242
# ## Local First Party Imports ----
4343
from toolbox_python.checkers import assert_is_valid
44-
from toolbox_python.collection_types import str_list
4544

4645

4746
## --------------------------------------------------------------------------- #
4847
## Exports ####
4948
## --------------------------------------------------------------------------- #
5049

5150

52-
__all__: str_list = ["generate_group_cutoffs"]
51+
__all__: list[str] = ["generate_group_cutoffs"]
5352

5453

5554
# ---------------------------------------------------------------------------- #

src/toolbox_python/lists.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,27 +39,21 @@
3939

4040

4141
# ## Python StdLib Imports ----
42+
from collections.abc import Collection
4243
from itertools import product as itertools_product
43-
from typing import Any, Optional, Union
44+
from typing import Any, Optional
4445

4546
# ## Python Third Party Imports ----
4647
from more_itertools import collapse as itertools_collapse
4748
from typeguard import typechecked
4849

49-
# ## Local First Party Imports ----
50-
from toolbox_python.collection_types import (
51-
any_list,
52-
collection,
53-
scalar,
54-
str_list,
55-
)
56-
5750

5851
# ---------------------------------------------------------------------------- #
5952
# Exports ####
6053
# ---------------------------------------------------------------------------- #
6154

62-
__all__: str_list = ["flatten", "flat_list", "product"]
55+
56+
__all__: list[str] = ["flatten", "flat_list", "product"]
6357

6458

6559
# ---------------------------------------------------------------------------- #
@@ -71,10 +65,10 @@
7165

7266
@typechecked
7367
def flatten(
74-
list_of_lists: Union[scalar, collection],
68+
list_of_lists: Collection[Collection[Any]],
7569
base_type: Optional[type] = None,
7670
levels: Optional[int] = None,
77-
) -> any_list:
71+
) -> list[Any]:
7872
"""
7973
!!! note "Summary"
8074
For a given `#!py list` of `#!py list`'s, flatten it out to be a single `#!py list`.
@@ -86,7 +80,7 @@ def flatten(
8680
[more_itertools.collapse]: https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse
8781
8882
Params:
89-
list_of_lists (Union[scalar, collection]):
83+
list_of_lists (Collection[Collection[Any]]):
9084
The input `#!py list` of `#!py list`'s that you'd like to flatten to a single-level `#!py list`.
9185
base_type (Optional[type], optional):
9286
Binary and text strings are not considered iterable and will not be collapsed. To avoid collapsing other types, specify `base_type`.<br>
@@ -195,15 +189,15 @@ def flatten(
195189
"""
196190
return list(
197191
itertools_collapse(
198-
iterable=list_of_lists, # type: ignore
192+
iterable=list_of_lists,
199193
base_type=base_type,
200194
levels=levels,
201195
)
202196
)
203197

204198

205199
@typechecked
206-
def flat_list(*inputs: Any) -> any_list:
200+
def flat_list(*inputs: Any) -> list[Any]:
207201
"""
208202
!!! note "Summary"
209203
Take in any number of inputs, and output them all in to a single flat `#!py list`.
@@ -217,7 +211,7 @@ def flat_list(*inputs: Any) -> any_list:
217211
If any of the inputs parsed to the parameters of this function are not the correct type. Uses the [`@typeguard.typechecked`](https://typeguard.readthedocs.io/en/stable/api.html#typeguard.typechecked) decorator.
218212
219213
Returns:
220-
(any_list):
214+
(list[Any]):
221215
The input having been coerced to a single flat `#!py list`.
222216
223217
???+ example "Examples"

src/toolbox_python/output.py

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040

4141
# ## Python StdLib Imports ----
42-
from collections.abc import Generator
42+
from collections.abc import Collection, Generator
4343
from logging import Logger, _nameToLevel
4444
from math import ceil
4545
from typing import Any, Literal, Optional, Union, overload
@@ -54,21 +54,31 @@
5454
assert_is_valid,
5555
is_type,
5656
)
57-
from toolbox_python.collection_types import (
58-
any_list,
59-
any_set,
60-
any_tuple,
61-
log_levels,
62-
str_list,
63-
)
6457

6558

6659
# ---------------------------------------------------------------------------- #
6760
# Exports ####
6861
# ---------------------------------------------------------------------------- #
6962

7063

71-
__all__: str_list = ["print_or_log_output", "list_columns"]
64+
__all__: list[str] = ["print_or_log_output", "list_columns", "log_levels"]
65+
66+
67+
## --------------------------------------------------------------------------- #
68+
## Constants ####
69+
## --------------------------------------------------------------------------- #
70+
71+
72+
log_levels = Literal["debug", "info", "warning", "error", "critical"]
73+
"""
74+
!!! note "Summary"
75+
To streamline other functions, this `type` alias is created for all of the `log` levels available.
76+
!!! abstract "Details"
77+
The structure of the `type` is as follows:
78+
```pycon {.py .python linenums="1" title="Type structure"}
79+
Literal["debug", "info", "warning", "error", "critical"]
80+
```
81+
"""
7282

7383

7484
# ---------------------------------------------------------------------------- #
@@ -265,38 +275,20 @@ def print_or_log_output(
265275
return None
266276

267277

268-
@overload
269-
@typechecked
270-
def list_columns(
271-
obj: Union[any_list, any_set, any_tuple, Generator],
272-
cols_wide: int = 4,
273-
columnwise: bool = True,
274-
gap: int = 4,
275-
print_output: Literal[False] = False,
276-
) -> str: ...
277-
@overload
278-
@typechecked
279-
def list_columns(
280-
obj: Union[any_list, any_set, any_tuple, Generator],
281-
cols_wide: int = 4,
282-
columnwise: bool = True,
283-
gap: int = 4,
284-
print_output: Literal[True] = True,
285-
) -> None: ...
286278
@typechecked
287279
def list_columns(
288-
obj: Union[any_list, any_set, any_tuple, Generator],
280+
obj: Union[Collection[Any], Generator],
289281
cols_wide: int = 4,
290282
columnwise: bool = True,
291283
gap: int = 4,
292-
print_output: bool = False,
284+
print_output: Literal[True, False] = False,
293285
) -> Optional[str]:
294286
"""
295287
!!! note "Summary"
296288
Print the given list in evenly-spaced columns.
297289
298290
Params:
299-
obj (Union[any_list, any_set, any_tuple, Generator]):
291+
obj (Union[Collection[Any], Generator]):
300292
The list to be formatted.
301293
302294
cols_wide (int, optional):
@@ -317,7 +309,7 @@ def list_columns(
317309
between columns based on the maximum `#!py len()` of the list items.<br>
318310
Defaults to: `#!py 4`.
319311
320-
print_output (bool, optional):
312+
print_output (Literal[True, False], optional):
321313
Whether or not to print the output to the terminal.
322314
323315
- `#!py True`: Will print and return.
@@ -433,7 +425,7 @@ def list_columns(
433425
assert_is_valid(gap, ">", 0)
434426

435427
# Prepare the string representation of the object
436-
string_list: str_list = [str(item) for item in obj]
428+
string_list: list[str] = [str(item) for item in obj]
437429
cols_wide = min(cols_wide, len(string_list))
438430
max_len: int = max(len(item) for item in string_list)
439431

@@ -442,15 +434,15 @@ def list_columns(
442434
cols_wide = int(ceil(len(string_list) / cols_wide))
443435

444436
# Segment the list into chunks
445-
segmented_list: list[str_list] = [
437+
segmented_list: list[list[str]] = [
446438
string_list[index : index + cols_wide] for index in range(0, len(string_list), cols_wide)
447439
]
448440

449441
# Ensure the last segment has the correct number of columns
450442
if columnwise:
451443
if len(segmented_list[-1]) != cols_wide:
452444
segmented_list[-1].extend([""] * (len(string_list) - len(segmented_list[-1])))
453-
combined_list: Union[list[str_list], Any] = zip(*segmented_list)
445+
combined_list: Union[list[list[str]], Any] = zip(*segmented_list)
454446
else:
455447
combined_list = segmented_list
456448

0 commit comments

Comments
 (0)