Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9db39003c4 | ||
|
|
4ffe372434 | ||
|
|
a00c27c465 | ||
|
|
1f7f4b8d53 | ||
|
|
baca79ce82 | ||
|
|
4265be6430 | ||
|
|
c16b086467 | ||
|
|
48a98c0206 | ||
|
|
f1788f057f |
@@ -1,7 +1,7 @@
|
|||||||
# MARK: Project info
|
# MARK: Project info
|
||||||
[project]
|
[project]
|
||||||
name = "corelibs"
|
name = "corelibs"
|
||||||
version = "0.39.0"
|
version = "0.41.0"
|
||||||
description = "Collection of utils for Python scripts"
|
description = "Collection of utils for Python scripts"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
@@ -13,7 +13,7 @@ dependencies = [
|
|||||||
"jmespath>=1.0.1",
|
"jmespath>=1.0.1",
|
||||||
"jsonpath-ng>=1.7.0",
|
"jsonpath-ng>=1.7.0",
|
||||||
"psutil>=7.0.0",
|
"psutil>=7.0.0",
|
||||||
"requests>=2.32.4",
|
"requests[proxy]>=2.32.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
# MARK: build system
|
# MARK: build system
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ class SettingsLoader:
|
|||||||
entry_split_char: dict[str, str] = {}
|
entry_split_char: dict[str, str] = {}
|
||||||
# entries that should be converted
|
# entries that should be converted
|
||||||
entry_convert: dict[str, str] = {}
|
entry_convert: dict[str, str] = {}
|
||||||
|
# no args to set
|
||||||
|
args_overrride: list[str] = []
|
||||||
# all the settings for the config id given
|
# all the settings for the config id given
|
||||||
settings: dict[str, dict[str, Any]] = {
|
settings: dict[str, dict[str, Any]] = {
|
||||||
config_id: {},
|
config_id: {},
|
||||||
@@ -162,6 +164,8 @@ class SettingsLoader:
|
|||||||
f"[!] In [{config_id}] the split character setup for entry failed: {check}: {e}",
|
f"[!] In [{config_id}] the split character setup for entry failed: {check}: {e}",
|
||||||
'CRITICAL'
|
'CRITICAL'
|
||||||
)) from e
|
)) from e
|
||||||
|
if check == "args_override:yes":
|
||||||
|
args_overrride.append(key)
|
||||||
if skip:
|
if skip:
|
||||||
continue
|
continue
|
||||||
settings[config_id][key] = [
|
settings[config_id][key] = [
|
||||||
@@ -185,9 +189,16 @@ class SettingsLoader:
|
|||||||
error: bool = False
|
error: bool = False
|
||||||
for entry, validate in config_validate.items():
|
for entry, validate in config_validate.items():
|
||||||
# if we have command line option set, this one overrides config
|
# if we have command line option set, this one overrides config
|
||||||
if self.__get_arg(entry):
|
if (args_entry := self.__get_arg(entry)) is not None:
|
||||||
self.__print(f"[*] Command line option override for: {entry}", 'WARNING')
|
self.__print(f"[*] Command line option override for: {entry}", 'WARNING')
|
||||||
settings[config_id][entry] = self.args.get(entry)
|
if (
|
||||||
|
# only set if flagged as allowed override from args
|
||||||
|
entry in args_overrride and
|
||||||
|
(isinstance(args_entry, list) and entry_split_char.get(entry)) or
|
||||||
|
(not isinstance(args_entry, list) and not entry_split_char.get(entry))
|
||||||
|
):
|
||||||
|
# args is list, but entry has not split, do not set
|
||||||
|
settings[config_id][entry] = args_entry
|
||||||
# validate checks
|
# validate checks
|
||||||
for check in validate:
|
for check in validate:
|
||||||
# CHECKS
|
# CHECKS
|
||||||
@@ -277,10 +288,8 @@ class SettingsLoader:
|
|||||||
elif convert_type in ["float", "any"] and is_float(settings[config_id][entry]):
|
elif convert_type in ["float", "any"] and is_float(settings[config_id][entry]):
|
||||||
settings[config_id][entry] = float(settings[config_id][entry])
|
settings[config_id][entry] = float(settings[config_id][entry])
|
||||||
elif convert_type in ["bool", "any"] and (
|
elif convert_type in ["bool", "any"] and (
|
||||||
settings[config_id][entry] == "true" or
|
settings[config_id][entry].lower() == "true" or
|
||||||
settings[config_id][entry] == "True" or
|
settings[config_id][entry].lower() == "false"
|
||||||
settings[config_id][entry] == "false" or
|
|
||||||
settings[config_id][entry] == "False"
|
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
settings[config_id][entry] = str_to_bool(settings[config_id][entry])
|
settings[config_id][entry] = str_to_bool(settings[config_id][entry])
|
||||||
|
|||||||
@@ -731,6 +731,7 @@ class Log(LogParent):
|
|||||||
# formatter_console = logging.Formatter(format_string, datefmt=format_date)
|
# formatter_console = logging.Formatter(format_string, datefmt=format_date)
|
||||||
formatter_console = logging.Formatter(format_string)
|
formatter_console = logging.Formatter(format_string)
|
||||||
self.__set_time_format_for_console_formatter(formatter_console, console_format_type)
|
self.__set_time_format_for_console_formatter(formatter_console, console_format_type)
|
||||||
|
self.log_settings['console_format_type'] = console_format_type
|
||||||
return formatter_console
|
return formatter_console
|
||||||
|
|
||||||
# MARK: console handler update
|
# MARK: console handler update
|
||||||
@@ -744,8 +745,12 @@ class Log(LogParent):
|
|||||||
Arguments:
|
Arguments:
|
||||||
console_format_type {ConsoleFormat} -- _description_
|
console_format_type {ConsoleFormat} -- _description_
|
||||||
"""
|
"""
|
||||||
|
# skip if console not enabled
|
||||||
if not self.log_settings['console_enabled']:
|
if not self.log_settings['console_enabled']:
|
||||||
return
|
return
|
||||||
|
# skip if format has not changed
|
||||||
|
if self.log_settings['console_format_type'] == console_format_type:
|
||||||
|
return
|
||||||
# update the formatter
|
# update the formatter
|
||||||
self.handlers[self.CONSOLE_HANDLER].setFormatter(
|
self.handlers[self.CONSOLE_HANDLER].setFormatter(
|
||||||
self.__set_console_formatter(console_format_type)
|
self.__set_console_formatter(console_format_type)
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
[TestA]
|
[TestA]
|
||||||
foo=bar
|
foo=bar
|
||||||
|
overload_from_args=bar
|
||||||
foobar=1
|
foobar=1
|
||||||
bar=st
|
bar=st
|
||||||
|
arg_overload=should_not_be_set_because_of_command_line_is_list
|
||||||
|
arg_overload_list=too,be,long
|
||||||
|
arg_overload_not_set=this should not be set because of override flag
|
||||||
|
just_values=too,be,long
|
||||||
some_match=foo
|
some_match=foo
|
||||||
some_match_list=foo,bar
|
some_match_list=foo,bar
|
||||||
test_list=a,b,c,d f, g h
|
test_list=a,b,c,d f, g h
|
||||||
|
|||||||
@@ -39,7 +39,10 @@ def main():
|
|||||||
|
|
||||||
sl = SettingsLoader(
|
sl = SettingsLoader(
|
||||||
{
|
{
|
||||||
'foo': 'OVERLOAD'
|
'overload_from_args': 'OVERLOAD from ARGS',
|
||||||
|
'arg_overload': ['should', 'not', 'be', 'set'],
|
||||||
|
'arg_overload_list': ['overload', 'this', 'list'],
|
||||||
|
'arg_overload_not_set': "DO_NOT_SET",
|
||||||
},
|
},
|
||||||
ROOT_PATH.joinpath(CONFIG_DIR, CONFIG_FILE),
|
ROOT_PATH.joinpath(CONFIG_DIR, CONFIG_FILE),
|
||||||
log=log
|
log=log
|
||||||
@@ -50,9 +53,11 @@ def main():
|
|||||||
config_load,
|
config_load,
|
||||||
{
|
{
|
||||||
# "doesnt": ["split:,"],
|
# "doesnt": ["split:,"],
|
||||||
"foo": ["mandatory:yes"],
|
"overload_from_args": ["args_override:yes", "mandatory:yes"],
|
||||||
"foobar": ["check:int"],
|
"foobar": ["check:int"],
|
||||||
"bar": ["mandatory:yes"],
|
"bar": ["mandatory:yes"],
|
||||||
|
"arg_overload_list": ["args_override:yes", "split:,",],
|
||||||
|
"arg_overload_not_set": [],
|
||||||
"some_match": ["matching:foo|bar"],
|
"some_match": ["matching:foo|bar"],
|
||||||
"some_match_list": ["split:,", "matching:foo|bar"],
|
"some_match_list": ["split:,", "matching:foo|bar"],
|
||||||
"test_list": [
|
"test_list": [
|
||||||
|
|||||||
@@ -113,6 +113,8 @@ def main():
|
|||||||
log.debug('Current logging format: %s', log.log_settings['console_format_type'])
|
log.debug('Current logging format: %s', log.log_settings['console_format_type'])
|
||||||
log.update_console_formatter(ConsoleFormat.TIME | ConsoleFormat.LINENO)
|
log.update_console_formatter(ConsoleFormat.TIME | ConsoleFormat.LINENO)
|
||||||
log.info('Does hit show less')
|
log.info('Does hit show less')
|
||||||
|
log.update_console_formatter(ConsoleFormat.TIME | ConsoleFormat.LINENO)
|
||||||
|
log.info('Does hit show less B')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -528,6 +528,123 @@ class TestLoadSettings:
|
|||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert "Command line option override" in captured.out
|
assert "Command line option override" in captured.out
|
||||||
|
|
||||||
|
def test_load_settings_args_no_flag(self, tmp_path: Path, capsys: CaptureFixture[str]):
|
||||||
|
"""Test default behavior (no args_override:yes) with list argument that has split"""
|
||||||
|
config_file = tmp_path / "test.ini"
|
||||||
|
config_file.write_text("[TestSection]\nvalue=a,b,c\n")
|
||||||
|
|
||||||
|
loader = SettingsLoader(
|
||||||
|
args={"value": ["x", "y", "z"]},
|
||||||
|
config_file=config_file
|
||||||
|
)
|
||||||
|
result = loader.load_settings(
|
||||||
|
"TestSection",
|
||||||
|
{"value": ["split:,"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Without args_override:yes flag, should use config value (no override)
|
||||||
|
assert result["value"] == ["a", "b", "c"]
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
# Message is printed but without args_override:yes flag, override doesn't happen
|
||||||
|
assert "Command line option override" in captured.out
|
||||||
|
|
||||||
|
def test_load_settings_args_list_no_split(self, tmp_path: Path, capsys: CaptureFixture[str]):
|
||||||
|
"""Test that list arguments without split entry are skipped"""
|
||||||
|
config_file = tmp_path / "test.ini"
|
||||||
|
config_file.write_text("[TestSection]\nvalue=config_value\n")
|
||||||
|
|
||||||
|
loader = SettingsLoader(
|
||||||
|
args={"value": ["arg1", "arg2", "arg3"]},
|
||||||
|
config_file=config_file
|
||||||
|
)
|
||||||
|
result = loader.load_settings(
|
||||||
|
"TestSection",
|
||||||
|
{"value": []}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should keep config value since args is list but no split defined
|
||||||
|
assert result["value"] == "config_value"
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
# Message is printed but list without split prevents the override
|
||||||
|
assert "Command line option override" in captured.out
|
||||||
|
|
||||||
|
def test_load_settings_args_list_with_split(self, tmp_path: Path, capsys: CaptureFixture[str]):
|
||||||
|
"""Test that list arguments with split entry and args_override:yes are applied"""
|
||||||
|
config_file = tmp_path / "test.ini"
|
||||||
|
config_file.write_text("[TestSection]\nvalue=a,b,c\n")
|
||||||
|
|
||||||
|
loader = SettingsLoader(
|
||||||
|
args={"value": ["arg1", "arg2", "arg3"]},
|
||||||
|
config_file=config_file
|
||||||
|
)
|
||||||
|
result = loader.load_settings(
|
||||||
|
"TestSection",
|
||||||
|
{"value": ["split:,", "args_override:yes"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should use args value because split is defined AND args_override:yes is set
|
||||||
|
assert result["value"] == ["arg1", "arg2", "arg3"]
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "Command line option override" in captured.out
|
||||||
|
|
||||||
|
def test_load_settings_args_no_with_mandatory(self, tmp_path: Path, capsys: CaptureFixture[str]):
|
||||||
|
"""Test default behavior (no args_override:yes) with mandatory field and list args with split"""
|
||||||
|
config_file = tmp_path / "test.ini"
|
||||||
|
config_file.write_text("[TestSection]\nvalue=config1,config2\n")
|
||||||
|
|
||||||
|
loader = SettingsLoader(
|
||||||
|
args={"value": ["arg1", "arg2"]},
|
||||||
|
config_file=config_file
|
||||||
|
)
|
||||||
|
result = loader.load_settings(
|
||||||
|
"TestSection",
|
||||||
|
{"value": ["mandatory:yes", "split:,"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should use config value because args_override:yes is not set (default: no override)
|
||||||
|
assert result["value"] == ["config1", "config2"]
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
# Message is printed but without args_override:yes flag, override doesn't happen
|
||||||
|
assert "Command line option override" in captured.out
|
||||||
|
|
||||||
|
def test_load_settings_args_no_with_mandatory_valid(self, tmp_path: Path, capsys: CaptureFixture[str]):
|
||||||
|
"""Test default behavior with string args (always overrides due to current logic)"""
|
||||||
|
config_file = tmp_path / "test.ini"
|
||||||
|
config_file.write_text("[TestSection]\nvalue=config_value\n")
|
||||||
|
|
||||||
|
loader = SettingsLoader(
|
||||||
|
args={"value": "arg_value"},
|
||||||
|
config_file=config_file
|
||||||
|
)
|
||||||
|
result = loader.load_settings(
|
||||||
|
"TestSection",
|
||||||
|
{"value": ["mandatory:yes"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Current behavior: string args without split always override (regardless of args_override:yes)
|
||||||
|
assert result["value"] == "arg_value"
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "Command line option override" in captured.out
|
||||||
|
|
||||||
|
def test_load_settings_args_string_no_split(self, tmp_path: Path, capsys: CaptureFixture[str]):
|
||||||
|
"""Test that string arguments with args_override:yes work normally"""
|
||||||
|
config_file = tmp_path / "test.ini"
|
||||||
|
config_file.write_text("[TestSection]\nvalue=config_value\n")
|
||||||
|
|
||||||
|
loader = SettingsLoader(
|
||||||
|
args={"value": "arg_value"},
|
||||||
|
config_file=config_file
|
||||||
|
)
|
||||||
|
result = loader.load_settings(
|
||||||
|
"TestSection",
|
||||||
|
{"value": ["args_override:yes"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should use args value for non-list args with args_override:yes
|
||||||
|
assert result["value"] == "arg_value"
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "Command line option override" in captured.out
|
||||||
|
|
||||||
def test_load_settings_no_config_file_with_args(self, tmp_path: Path):
|
def test_load_settings_no_config_file_with_args(self, tmp_path: Path):
|
||||||
"""Test loading settings without config file but with mandatory args"""
|
"""Test loading settings without config file but with mandatory args"""
|
||||||
config_file = tmp_path / "missing.ini"
|
config_file = tmp_path / "missing.ini"
|
||||||
@@ -704,5 +821,48 @@ class TestComplexScenarios:
|
|||||||
assert result["emails"] == "test@example.com"
|
assert result["emails"] == "test@example.com"
|
||||||
assert result["date"] == "2025-01-15"
|
assert result["date"] == "2025-01-15"
|
||||||
|
|
||||||
|
def test_args_no_and_list_skip_combination(self, tmp_path: Path, capsys: CaptureFixture[str]):
|
||||||
|
"""Test combination of args_override:yes flag and list argument skip behavior"""
|
||||||
|
config_file = tmp_path / "test.ini"
|
||||||
|
config_file.write_text(
|
||||||
|
"[Settings]\n"
|
||||||
|
"no_override=a,b,c\n"
|
||||||
|
"list_no_split=config_list\n"
|
||||||
|
"list_with_split=x,y,z\n"
|
||||||
|
"normal=config_normal\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
loader = SettingsLoader(
|
||||||
|
args={
|
||||||
|
"no_override": ["arg1", "arg2"],
|
||||||
|
"list_no_split": ["arg1", "arg2"],
|
||||||
|
"list_with_split": ["p", "q", "r"],
|
||||||
|
"normal": "arg_normal"
|
||||||
|
},
|
||||||
|
config_file=config_file
|
||||||
|
)
|
||||||
|
result = loader.load_settings(
|
||||||
|
"Settings",
|
||||||
|
{
|
||||||
|
"no_override": ["split:,"],
|
||||||
|
"list_no_split": [],
|
||||||
|
"list_with_split": ["split:,", "args_override:yes"],
|
||||||
|
"normal": ["args_override:yes"]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Should use config value (no args_override:yes flag for list with split)
|
||||||
|
assert result["no_override"] == ["a", "b", "c"]
|
||||||
|
# Should use config value because args is list without split
|
||||||
|
assert result["list_no_split"] == "config_list"
|
||||||
|
# Should use args value because split is defined AND args_override:yes is set
|
||||||
|
assert result["list_with_split"] == ["p", "q", "r"]
|
||||||
|
# Should use args value (args_override:yes set for string arg)
|
||||||
|
assert result["normal"] == "arg_normal"
|
||||||
|
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
# Should see override messages (even though list_no_split prints, it doesn't apply)
|
||||||
|
assert "Command line option override" in captured.out
|
||||||
|
|
||||||
|
|
||||||
# __END__
|
# __END__
|
||||||
|
|||||||
4
uv.lock
generated
4
uv.lock
generated
@@ -108,7 +108,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "corelibs"
|
name = "corelibs"
|
||||||
version = "0.39.0"
|
version = "0.41.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "corelibs-datetime" },
|
{ name = "corelibs-datetime" },
|
||||||
@@ -137,7 +137,7 @@ requires-dist = [
|
|||||||
{ name = "jmespath", specifier = ">=1.0.1" },
|
{ name = "jmespath", specifier = ">=1.0.1" },
|
||||||
{ name = "jsonpath-ng", specifier = ">=1.7.0" },
|
{ name = "jsonpath-ng", specifier = ">=1.7.0" },
|
||||||
{ name = "psutil", specifier = ">=7.0.0" },
|
{ name = "psutil", specifier = ">=7.0.0" },
|
||||||
{ name = "requests", specifier = ">=2.32.4" },
|
{ name = "requests", extras = ["proxy"], specifier = ">=2.32.4" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata.requires-dev]
|
[package.metadata.requires-dev]
|
||||||
|
|||||||
Reference in New Issue
Block a user