pythonwrench 0.6.4__tar.gz → 0.6.5__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.
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/CHANGELOG.md +9 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/CITATION.cff +2 -2
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/PKG-INFO +1 -1
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/__init__.py +1 -1
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/argparse/dataclass_.py +24 -1
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/logging.py +51 -17
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/time.py +17 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_argparse.py +16 -2
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/.github/workflows/test.yaml +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/.gitignore +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/.pre-commit-config.yaml +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/.readthedocs.yaml +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/LICENSE +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/README.md +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/Makefile +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/conf.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/index.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/make.bat +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/modules.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.abc.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.argparse.dataclass_.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.argparse.parsers.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.argparse.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.cast.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.checksum.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.collections.collections.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.collections.prop.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.collections.reducers.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.collections.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.concurrent.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.csv.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.dataclasses.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.datetime.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.difflib.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.disk_cache.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.enum.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.functools.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.hashlib.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.importlib.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.inspect.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.json.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.jsonl.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.logging.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.math.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.os.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.pickle.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.random.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.re.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.semver.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.serialization.csv.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.serialization.json.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.serialization.jsonl.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.serialization.pickle.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.serialization.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.time.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.typing.checks.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.typing.classes.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.typing.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/pythonwrench.warnings.rst +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/docs/update.bash +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/pyproject.toml +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/setup.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/__main__.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/_core.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/abc.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/argparse/__init__.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/argparse/parsers.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/cast.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/checksum.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/collections/__init__.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/collections/collections.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/collections/prop.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/collections/reducers.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/concurrent.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/csv.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/dataclasses.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/datetime.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/difflib.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/disk_cache.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/entrypoints/info.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/entrypoints/safe_rmdir.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/entrypoints/tree.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/enum.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/functools.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/hashlib.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/importlib.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/inspect.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/json.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/jsonl.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/math.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/os.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/pickle.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/random.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/re.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/semver.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/serialization/__init__.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/serialization/_core.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/serialization/csv.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/serialization/json.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/serialization/jsonl.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/serialization/pickle.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/typing/__init__.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/typing/checks.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/typing/classes.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/src/pythonwrench/warnings.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_abc.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_cast.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_checksum.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_collections.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_core.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_csv.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_dataclasses.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_difflib.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_disk_cache.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_entrypoints.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_enum.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_functools.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_hashlib.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_importlib.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_inspect.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_json.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_jsonl.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_logging.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_math.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_os.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_random.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_readme.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_semver.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/tests/test_typing.py +0 -0
- {pythonwrench-0.6.4 → pythonwrench-0.6.5}/uv.lock +0 -0
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.6.5] 2026-08-20
|
|
6
|
+
### Added
|
|
7
|
+
- `return_single_namespace` option for `parse_args_using_dataclass` function.
|
|
8
|
+
- Context manager for `Ticker` class.
|
|
9
|
+
|
|
10
|
+
### Modified
|
|
11
|
+
- Allow argument `stream` to be None or root_logging_stream customize handlers set in setup_logging functions.
|
|
12
|
+
|
|
13
|
+
|
|
5
14
|
## [0.6.4] 2026-08-17
|
|
6
15
|
### Added
|
|
7
16
|
- Function `parse_args_using_dataclass` can now handle multiple dataclass at once.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pythonwrench
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
4
4
|
Summary: Python library with tools for typing, manipulating collections, and more!
|
|
5
5
|
Project-URL: Homepage, https://pypi.org/project/pythonwrench/
|
|
6
6
|
Project-URL: Documentation, https://pythonwrench.readthedocs.io/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
|
-
from argparse import ArgumentParser
|
|
4
|
+
from argparse import ArgumentParser, Namespace
|
|
5
5
|
from dataclasses import MISSING, fields
|
|
6
6
|
from typing import (
|
|
7
7
|
Any,
|
|
@@ -48,6 +48,19 @@ _BoolActionName = Literal["store", "store_true", "store_false", "bool_optional"]
|
|
|
48
48
|
BoolAction = Union[_BoolActionName, Type[BooleanOptionalAction]]
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
@overload
|
|
52
|
+
def parse_args_using_dataclass(
|
|
53
|
+
dataclass_type: Type[DataclassInstance],
|
|
54
|
+
*dataclass_types: Type[DataclassInstance],
|
|
55
|
+
args: Optional[Iterable[str]] = None,
|
|
56
|
+
parser: Optional[ArgumentParser] = None,
|
|
57
|
+
list_parsing: ListParsing = "argparse",
|
|
58
|
+
bool_action: BoolAction = "store",
|
|
59
|
+
add_dashed_arg: bool = True,
|
|
60
|
+
return_single_namespace: Literal[True],
|
|
61
|
+
) -> Namespace: ...
|
|
62
|
+
|
|
63
|
+
|
|
51
64
|
@overload
|
|
52
65
|
def parse_args_using_dataclass(
|
|
53
66
|
dataclass_type: Type[T_DataclassInstance],
|
|
@@ -57,6 +70,7 @@ def parse_args_using_dataclass(
|
|
|
57
70
|
list_parsing: ListParsing = "argparse",
|
|
58
71
|
bool_action: BoolAction = "store",
|
|
59
72
|
add_dashed_arg: bool = True,
|
|
73
|
+
return_single_namespace: Literal[False] = False,
|
|
60
74
|
) -> T_DataclassInstance: ...
|
|
61
75
|
|
|
62
76
|
|
|
@@ -71,6 +85,7 @@ def parse_args_using_dataclass(
|
|
|
71
85
|
list_parsing: ListParsing = "argparse",
|
|
72
86
|
bool_action: BoolAction = "store",
|
|
73
87
|
add_dashed_arg: bool = True,
|
|
88
|
+
return_single_namespace: Literal[False] = False,
|
|
74
89
|
) -> Tuple[
|
|
75
90
|
T_DataclassInstance,
|
|
76
91
|
T_DataclassInstance_2,
|
|
@@ -89,6 +104,7 @@ def parse_args_using_dataclass(
|
|
|
89
104
|
list_parsing: ListParsing = "argparse",
|
|
90
105
|
bool_action: BoolAction = "store",
|
|
91
106
|
add_dashed_arg: bool = True,
|
|
107
|
+
return_single_namespace: Literal[False] = False,
|
|
92
108
|
) -> Tuple[
|
|
93
109
|
T_DataclassInstance,
|
|
94
110
|
T_DataclassInstance_2,
|
|
@@ -109,6 +125,7 @@ def parse_args_using_dataclass(
|
|
|
109
125
|
list_parsing: ListParsing = "argparse",
|
|
110
126
|
bool_action: BoolAction = "store",
|
|
111
127
|
add_dashed_arg: bool = True,
|
|
128
|
+
return_single_namespace: Literal[False] = False,
|
|
112
129
|
) -> Tuple[
|
|
113
130
|
T_DataclassInstance,
|
|
114
131
|
T_DataclassInstance_2,
|
|
@@ -131,6 +148,7 @@ def parse_args_using_dataclass(
|
|
|
131
148
|
list_parsing: ListParsing = "argparse",
|
|
132
149
|
bool_action: BoolAction = "store",
|
|
133
150
|
add_dashed_arg: bool = True,
|
|
151
|
+
return_single_namespace: Literal[False] = False,
|
|
134
152
|
) -> Tuple[
|
|
135
153
|
T_DataclassInstance,
|
|
136
154
|
T_DataclassInstance_2,
|
|
@@ -148,9 +166,11 @@ def parse_args_using_dataclass(
|
|
|
148
166
|
list_parsing: ListParsing = "argparse",
|
|
149
167
|
bool_action: BoolAction = "store",
|
|
150
168
|
add_dashed_arg: bool = True,
|
|
169
|
+
return_single_namespace: bool = False,
|
|
151
170
|
) -> Union[
|
|
152
171
|
DataclassInstance,
|
|
153
172
|
Tuple[DataclassInstance, ...],
|
|
173
|
+
Namespace,
|
|
154
174
|
]:
|
|
155
175
|
"""Converts prog args to a typed dataclass using argparse.
|
|
156
176
|
|
|
@@ -175,6 +195,9 @@ def parse_args_using_dataclass(
|
|
|
175
195
|
msg = f"Found {len(argv)} unknown arguments: {argv}."
|
|
176
196
|
raise ValueError(msg)
|
|
177
197
|
|
|
198
|
+
if return_single_namespace:
|
|
199
|
+
return parsed
|
|
200
|
+
|
|
178
201
|
dataclass_insts = []
|
|
179
202
|
for dataclass_type_i in dataclass_types:
|
|
180
203
|
if init_parser is None and len(dataclass_types) == 1:
|
|
@@ -8,13 +8,13 @@ from functools import lru_cache
|
|
|
8
8
|
from logging import FileHandler, Formatter, Logger, StreamHandler
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from types import ModuleType
|
|
11
|
-
from typing import IO, List, Literal, Optional, TypeVar, Union
|
|
11
|
+
from typing import IO, Iterable, List, Literal, Optional, TypeVar, Union
|
|
12
12
|
|
|
13
13
|
from typing_extensions import TypeAlias
|
|
14
14
|
|
|
15
15
|
from pythonwrench.importlib import reload_submodules
|
|
16
16
|
from pythonwrench.semver import Version
|
|
17
|
-
from pythonwrench.typing import SupportsIterLen
|
|
17
|
+
from pythonwrench.typing import SupportsIterLen, isinstance_generic
|
|
18
18
|
|
|
19
19
|
T = TypeVar("T", covariant=True)
|
|
20
20
|
|
|
@@ -29,6 +29,9 @@ PackageOrLoggerList: TypeAlias = Union[
|
|
|
29
29
|
PackageOrLogger,
|
|
30
30
|
SupportsIterLen[PackageOrLogger],
|
|
31
31
|
]
|
|
32
|
+
TargetStream = Union[
|
|
33
|
+
IO[str], Iterable[IO[str]], Literal["auto", "root_logger_streams"], None
|
|
34
|
+
]
|
|
32
35
|
|
|
33
36
|
_PARENT_FILE_KEY = "__parent_file__"
|
|
34
37
|
DEFAULT_FMT = "[%(asctime)s][%(name)s][%(levelname)s] - %(message)s"
|
|
@@ -58,7 +61,7 @@ def setup_logging_verbose(
|
|
|
58
61
|
verbose: Optional[int] = VERBOSE_INFO,
|
|
59
62
|
*,
|
|
60
63
|
fmt: Union[str, None, Formatter] = DEFAULT_FMT,
|
|
61
|
-
stream:
|
|
64
|
+
stream: TargetStream = "auto",
|
|
62
65
|
set_fmt: bool = True,
|
|
63
66
|
capture_warnings: bool = True,
|
|
64
67
|
autoreload: bool = True,
|
|
@@ -88,7 +91,7 @@ def setup_logging_level(
|
|
|
88
91
|
level: Optional[int] = logging.INFO,
|
|
89
92
|
*,
|
|
90
93
|
fmt: Union[str, None, Formatter] = DEFAULT_FMT,
|
|
91
|
-
stream:
|
|
94
|
+
stream: TargetStream = "auto",
|
|
92
95
|
set_fmt: bool = True,
|
|
93
96
|
capture_warnings: bool = True,
|
|
94
97
|
autoreload: bool = True,
|
|
@@ -105,28 +108,59 @@ def setup_logging_level(
|
|
|
105
108
|
|
|
106
109
|
if stream == "auto":
|
|
107
110
|
if running_on_interpreter():
|
|
108
|
-
|
|
111
|
+
streams = [sys.stdout]
|
|
109
112
|
else:
|
|
110
|
-
|
|
113
|
+
streams = [sys.stderr]
|
|
114
|
+
elif stream == "root_logger_streams":
|
|
115
|
+
streams = [
|
|
116
|
+
handler.stream
|
|
117
|
+
for handler in logging.getLogger().handlers
|
|
118
|
+
if isinstance(handler, StreamHandler)
|
|
119
|
+
]
|
|
120
|
+
elif isinstance(stream, IO):
|
|
121
|
+
streams = [stream]
|
|
122
|
+
elif stream is None:
|
|
123
|
+
streams = stream
|
|
124
|
+
elif isinstance_generic(stream, Iterable[IO]):
|
|
125
|
+
streams = list(stream)
|
|
126
|
+
else:
|
|
127
|
+
raise TypeError(f"Invalid argument type {type(stream)=}.")
|
|
128
|
+
del stream
|
|
111
129
|
|
|
112
130
|
for logger in logger_lst:
|
|
113
|
-
if
|
|
114
|
-
|
|
131
|
+
if level is not None:
|
|
132
|
+
logger.setLevel(level)
|
|
115
133
|
|
|
134
|
+
if not set_fmt:
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
if streams is None:
|
|
116
138
|
for handler in logger.handlers:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
found = True
|
|
120
|
-
break
|
|
139
|
+
handler.setFormatter(fmt)
|
|
140
|
+
continue
|
|
121
141
|
|
|
122
|
-
|
|
123
|
-
|
|
142
|
+
found = [False] * len(streams)
|
|
143
|
+
|
|
144
|
+
for handler in logger.handlers:
|
|
145
|
+
if not isinstance(handler, StreamHandler):
|
|
146
|
+
continue
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
index = streams.index(handler.stream)
|
|
150
|
+
except IndexError:
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
handler.setFormatter(fmt)
|
|
154
|
+
found[index] = True
|
|
155
|
+
|
|
156
|
+
if not all(found):
|
|
157
|
+
for stream_i, found_i in zip(streams, found):
|
|
158
|
+
if found_i:
|
|
159
|
+
continue
|
|
160
|
+
handler = StreamHandler(stream_i)
|
|
124
161
|
handler.setFormatter(fmt)
|
|
125
162
|
logger.addHandler(handler)
|
|
126
163
|
|
|
127
|
-
if level is not None:
|
|
128
|
-
logger.setLevel(level)
|
|
129
|
-
|
|
130
164
|
if autoreload:
|
|
131
165
|
for logger in logger_lst:
|
|
132
166
|
if logger.name not in sys.modules:
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import time
|
|
5
5
|
from typing import Callable, Optional
|
|
6
6
|
|
|
7
|
+
from typing_extensions import Self
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
class Ticker:
|
|
9
11
|
def __init__(
|
|
@@ -32,3 +34,18 @@ class Ticker:
|
|
|
32
34
|
if prev_tick is None:
|
|
33
35
|
prev_tick = self._get_time_fn()
|
|
34
36
|
self._prev_tick = prev_tick
|
|
37
|
+
|
|
38
|
+
def get_elapsed_duration(self) -> float:
|
|
39
|
+
"""Returns elapsed time since last tick. If no tick has been set, returns 0."""
|
|
40
|
+
if self._prev_tick is None:
|
|
41
|
+
return 0.0
|
|
42
|
+
now = self._get_time_fn()
|
|
43
|
+
duration = now - self._prev_tick
|
|
44
|
+
return duration
|
|
45
|
+
|
|
46
|
+
def __enter__(self) -> Self:
|
|
47
|
+
self.tick()
|
|
48
|
+
return self
|
|
49
|
+
|
|
50
|
+
def __exit__(self, exc_type, exc, tb):
|
|
51
|
+
pass
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
4
|
import unittest
|
|
5
|
-
from argparse import ArgumentParser
|
|
6
|
-
from dataclasses import dataclass, field
|
|
5
|
+
from argparse import ArgumentParser, Namespace
|
|
6
|
+
from dataclasses import asdict, dataclass, field
|
|
7
7
|
from enum import Enum, auto
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Iterable, List, Literal, Optional, Tuple, Union
|
|
@@ -302,6 +302,20 @@ class TestDataclassParser(TestCase):
|
|
|
302
302
|
assert cfg1 == expected_cfg1
|
|
303
303
|
assert cfg2 == expected_cfg2
|
|
304
304
|
|
|
305
|
+
args = parse_args_using_dataclass(
|
|
306
|
+
Cfg2,
|
|
307
|
+
Cfg1,
|
|
308
|
+
args=[
|
|
309
|
+
"--path",
|
|
310
|
+
str(expected_cfg1.path),
|
|
311
|
+
"--b",
|
|
312
|
+
expected_cfg2.b,
|
|
313
|
+
],
|
|
314
|
+
return_single_namespace=True,
|
|
315
|
+
)
|
|
316
|
+
expected_args = Namespace(**asdict(expected_cfg1), **asdict(expected_cfg2))
|
|
317
|
+
assert args == expected_args
|
|
318
|
+
|
|
305
319
|
def test_parse_store_bool(self) -> None:
|
|
306
320
|
@dataclass
|
|
307
321
|
class Cfg:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|