mentortools/libs/: init-helpers-abm-3.1.75045a0 metadata and description
Tools to parse args, read config, init logs and etc
| author | Mike Orlov |
| author_email | m.orlov@abm-jsc.ru |
| classifiers |
|
| description_content_type | text/markdown |
| requires_python | >=3.11,<4.0 |
| File | Tox results | History |
|---|---|---|
init_helpers_abm-3.1.75045a0-py3-none-any.whl
|
|
|
init_helpers_abm-3.1.75045a0.tar.gz
|
|
Init Helper
Tools to parse args, read config, init logs and etc
Копирование кода для тестов
cp -r $ORIGIN_DIR/init_helpers ./init_helpers
Парсинг конфигов
Создадим toml конфиг файл
printf 'some = 123\nother = "www"\nbar = "empty"' > example.toml
Проверим содержимое
cat example.toml
получим:
some = 123
other = "www"
bar = "empty"
Считаем конфиг как экземпляр датакласса
Подготовим python файл, имитирующий чтение конфига и печатающий результат
printf '
import dataclasses
import init_helpers
@dataclasses.dataclass
class Config:
other: str
some: int = 42
foo: float = 1.23
topics: tuple[str, ...] = ("one", )
config = init_helpers.parse_args_as_dataclass(Config)
print(config)
' > init.py
Запустим инициализацию, передав ей конфиг
python init.py example.toml
Получим
Config(other='www', some=123, foo=1.23, topics=('one',))
Запустим инициализацию с конфигом и переопределив значения из конфига
python init.py example.toml -v foo=4.2 -v some=42
Получим
Config(other='www', some=42, foo=4.2, topics=('one',))
Запустим инициализацию без конфига, передав необходимые значения через аргументы запуска
python init.py -v other=qqq -v 'topics=["two","four"]'
Получим
Config(other='qqq', some=42, foo=1.23, topics=('two', 'four'))
Создадим ini конфиг файл
printf '[main]\ntimeout = 10\nurl = http://\ntopics = ["ini"]' > example2.ini
Проверим содержимое
cat example2.ini
получим:
[main]
timeout = 10
url = http://
topics = ["ini"]
Считаем ini конфиг как экземпляр датакласса
Подготовим python файл, имитирующий чтение конфига и печатающий результат
printf '
import dataclasses
import init_helpers
@dataclasses.dataclass
class Config:
@dataclasses.dataclass
class MainConfig:
url: str
topics: tuple[str]
timeout: float = 5
main: MainConfig
config = init_helpers.parse_args_as_dataclass(Config)
print(config)
' > init.py
Запустим инициализацию, передав ей ini конфиг
python init.py example2.ini
Получим
Config(main=Config.MainConfig(url='http://', topics=('ini',), timeout=10.0))
Примечание
init_helpers.parse_args_as_dataclass парсит файл на основе расширения, поэтому один и тот же код может работать
и с .ini и .toml файлами. Для примера создадим toml файл и запустим с ним приложение:
printf '[main]\ntimeout = 10\nurl = "http://"\ntopics = ["toml"]' > example2.toml
python init.py example2.toml
Получим:
Config(main=Config.MainConfig(url='http://', topics=('toml',), timeout=10.0))
Conditional insert
Conditional append
Appends passed args to list passed as first argument, one by one checking condition.
Default condition is init_helpers.is_not_none. Example:
from init_helpers import conditional_append
conditional_append(target := [73, 123], 'value', None, 1, -2, 3)
print(target)
conditional_append(target := [73, 123], 'value', None, 1, -2, 3,
_condition=lambda x: isinstance(x, int) and x > 0)
print(target)
will produce:
[73, 123, 'value', 1, -2, 3]
[73, 123, 1, 3]
Conditional add
Adds passed args to set passed as first argument, one by one checking condition.
Default condition is init_helpers.is_not_none. Example:
from init_helpers import conditional_add
conditional_add(target := {73, 123}, None, 1, -2, 3)
print(target)
conditional_add(target := {73, 123}, 1, -2, 3, _condition=lambda x: x > 0)
print(target)
will produce:
{1, 3, 73, 123, -2}
{3, 73, 123, 1}
Conditional set
Sets passed kwargs to MutableMapping passed as first argument, one by one checking values by condition.
Default condition is init_helpers.is_not_none. Example:
from init_helpers import conditional_set
conditional_set(target := {'test': 'q'}, key='value', skip=None)
print(target)
conditional_set(target := {'test': 'q'}, key1=1, key2=2, skip1=-1, skip2=-2, _condition=lambda x: x > 0)
print(target)
will produce (sets do not keep ordering, so values are shuffled):
{'test': 'q', 'key': 'value'}
{'test': 'q', 'key1': 1, 'key2': 2}
CustomDumps
Extended version of json.dumps
Basic types serialise as in original
from init_helpers import custom_dumps
print("None:", custom_dumps(None))
print("int:", custom_dumps(42))
print("float:", custom_dumps(4.2))
print("bool:", custom_dumps(True))
print("list:", custom_dumps([42]))
print("dict:", custom_dumps({42: 4.2}))
will produce
None: null
int: 42
float: 4.2
bool: true
list: [42]
dict: {"42": 4.2}
Additional types serialise
import dataclasses
import zoneinfo
import datetime
from decimal import Decimal
from init_helpers import ReprInDumps, custom_dumps, read_json_file_by_path
print("set:", custom_dumps({42})) # serialised as list
print("Decimal:", custom_dumps(Decimal("1.20") + Decimal("1.30"))) # serialised as string
print("Exception:", custom_dumps(ValueError("payload"))) # serialised as string by repr method
print("bytes:", custom_dumps(bytes.fromhex('ffeeaa'))) # as base64 with prefix: data:application/octet-stream;base64,
print("time:", custom_dumps(datetime.time(12, 34, 56))) # serialised as iso string
print("date:", custom_dumps(datetime.date(2011, 11, 11))) # serialised as iso string
print("aware datetime:", custom_dumps(datetime.datetime(
2024, 11, 11, tzinfo=zoneinfo.ZoneInfo('Europe/Moscow')))) # tz-aware serialised as int millisecond timestamp
try:
custom_dumps(datetime.datetime(2024, 11, 11)) # naive datetime is disallowed
except TypeError as e:
print("naive datetime:", e)
@dataclasses.dataclass
class Foo:
a: int
b: str = 'www'
c: float = dataclasses.field(default=123, repr=False)
print("dataclass instance:", custom_dumps(Foo(42))) # serialised as dict WITHOUT field with repr=False
will produce
set: [42]
Decimal: "2.50"
Exception: {"_t": "PY::Exception", "key": "ValueError", "args": ["payload"]}
bytes: "data:application/octet-stream;base64,b'/+6q'"
time: "12:34:56"
date: "2011-11-11"
aware datetime: "2024-11-11T00:00:00+03:00"
naive datetime: TypeError: datetime.datetime WITHOUT tzinfo is not JSON serializable
dataclass instance: {"a": 42, "b": "www"}
Dataclass Protocol
Can be used to use isinstance on dataclasses. Example:
Create a file with typing based on DataclassProtocol
from typing import Any
from dataclasses import dataclass
from init_helpers import DataclassProtocol
def is_dc(obj: DataclassProtocol | Any) -> bool:
return isinstance(obj, DataclassProtocol)
@dataclass
class DC:
a: int = 1
class NonDC:
a: int
print(f'{is_dc(DC)=}')
print(f'{is_dc(NonDC)=}')
print(f'{is_dc(DC())=}')
print(f'{is_dc(NonDC())=}')
is_dc(DC)=True
is_dc(NonDC)=False
is_dc(DC())=True
is_dc(NonDC())=False