hadalized 0.4.0__py3-none-any.whl
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.
- hadalized/__init__.py +1 -0
- hadalized/__main__.py +6 -0
- hadalized/base.py +137 -0
- hadalized/cache.py +121 -0
- hadalized/cli/__init__.py +1 -0
- hadalized/cli/main.py +221 -0
- hadalized/color.py +595 -0
- hadalized/config.py +442 -0
- hadalized/const.py +6 -0
- hadalized/convert.py.bak +1903 -0
- hadalized/homedirs.py +69 -0
- hadalized/options.py +134 -0
- hadalized/palette.py +133 -0
- hadalized/templates/colors.html +21 -0
- hadalized/templates/config.toml +15 -0
- hadalized/templates/generic.txt +1 -0
- hadalized/templates/item.html +1 -0
- hadalized/templates/lua_module.lua +6 -0
- hadalized/templates/model_dump.json +1 -0
- hadalized/templates/neovim.lua +24 -0
- hadalized/templates/neovim_palette.lua +7 -0
- hadalized/templates/palette.html +53 -0
- hadalized/templates/palette_info.json +1 -0
- hadalized/templates/palette_test.toml +10 -0
- hadalized/templates/starship-all.toml +98 -0
- hadalized/templates/starship.toml +233 -0
- hadalized/templates/wezterm.toml +45 -0
- hadalized/web.py +168 -0
- hadalized/writer.py +243 -0
- hadalized-0.4.0.dist-info/METADATA +79 -0
- hadalized-0.4.0.dist-info/RECORD +33 -0
- hadalized-0.4.0.dist-info/WHEEL +4 -0
- hadalized-0.4.0.dist-info/entry_points.txt +4 -0
hadalized/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI app for generating color themes for specific applications."""
|
hadalized/__main__.py
ADDED
hadalized/base.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Base container for all model classes."""
|
|
2
|
+
|
|
3
|
+
from typing import ClassVar, Self
|
|
4
|
+
|
|
5
|
+
from pydantic import PrivateAttr
|
|
6
|
+
from pydantic_settings import (
|
|
7
|
+
BaseSettings,
|
|
8
|
+
PydanticBaseSettingsSource,
|
|
9
|
+
SettingsConfigDict,
|
|
10
|
+
# TomlConfigSettingsSource,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from hadalized.const import APP_NAME, APP_VERSION
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseNode(BaseSettings):
|
|
17
|
+
"""An extension of BaseSettings that all model classes inherit.
|
|
18
|
+
|
|
19
|
+
Unless overriden, by default only initialization settings are respected.
|
|
20
|
+
|
|
21
|
+
Full setting sources are exposed in the ``UserConfig`` subclass.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict(
|
|
25
|
+
frozen=True,
|
|
26
|
+
extra="forbid",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
_hash: int | None = PrivateAttr(default=None)
|
|
30
|
+
"""Cached hash computation so that instances can be passed to cached
|
|
31
|
+
functions and used in dicts."""
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def settings_customise_sources(
|
|
35
|
+
cls,
|
|
36
|
+
settings_cls: type[BaseSettings],
|
|
37
|
+
init_settings: PydanticBaseSettingsSource,
|
|
38
|
+
env_settings: PydanticBaseSettingsSource,
|
|
39
|
+
dotenv_settings: PydanticBaseSettingsSource,
|
|
40
|
+
file_secret_settings: PydanticBaseSettingsSource,
|
|
41
|
+
) -> tuple[PydanticBaseSettingsSource, ...]:
|
|
42
|
+
"""Set source loading priority.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Priority order in which config settings are loaded.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
return (init_settings,)
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def app_info(self) -> str:
|
|
52
|
+
"""App name and version."""
|
|
53
|
+
return f"{APP_NAME} v{APP_VERSION}"
|
|
54
|
+
|
|
55
|
+
def model_dump_lua(self) -> str:
|
|
56
|
+
"""Dump the model as a lua table.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
A human readable lua table string.
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
import luadata
|
|
63
|
+
|
|
64
|
+
# TODO: Unclear if we want to import luadata just for this
|
|
65
|
+
return luadata.serialize(self.model_dump(mode="json"), indent=" ")
|
|
66
|
+
|
|
67
|
+
def replace(self, **kwargs) -> Self:
|
|
68
|
+
"""Create a new instance with input arguments merged in.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
A new instance.
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
return self.model_validate(self.model_dump() | kwargs)
|
|
75
|
+
|
|
76
|
+
def __getitem__(self, key: str):
|
|
77
|
+
"""Provide dict-like lookup for all models.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
The field specified by the input key.
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
return getattr(self, key)
|
|
84
|
+
|
|
85
|
+
def __hash__(self) -> int:
|
|
86
|
+
"""Make an instance hashable for use in cache and dict lookups.
|
|
87
|
+
|
|
88
|
+
Defined for type checking purposes. Frozen models are hashable.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
The BaseModel hash.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
if self._hash is None:
|
|
95
|
+
self._hash = hash(self.model_dump_json())
|
|
96
|
+
return self._hash
|
|
97
|
+
|
|
98
|
+
def __len__(self) -> int:
|
|
99
|
+
"""Report the number of model fields.
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The length of the set of model fields.
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
return len(self.__class__.model_fields)
|
|
106
|
+
|
|
107
|
+
def __or__(self, other: BaseNode) -> Self:
|
|
108
|
+
"""Shallow merge explicitly set fields.
|
|
109
|
+
|
|
110
|
+
Only the explicitly set fields of ``other`` are merged in.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
other: An instance of the same type or parent type to ``self``.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
A new instance with the set fields of `other` merged in.
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
merged = self.model_dump(exclude_unset=True) | other.model_dump(
|
|
120
|
+
exclude_unset=True
|
|
121
|
+
)
|
|
122
|
+
return self.model_validate(merged)
|
|
123
|
+
|
|
124
|
+
# def merge(self, parent: BaseNode) -> Self:
|
|
125
|
+
# """Shallow merge a parent instance.
|
|
126
|
+
#
|
|
127
|
+
# Args:
|
|
128
|
+
# parent: A model with a subset of the fields defined in the
|
|
129
|
+
# instance.
|
|
130
|
+
#
|
|
131
|
+
# Returns:
|
|
132
|
+
# A new instance of the same type as the instance with the set
|
|
133
|
+
# parent fields.
|
|
134
|
+
#
|
|
135
|
+
# """
|
|
136
|
+
# merged = self.model_dump() | parent.model_dump(exclude_unset=True)
|
|
137
|
+
# return self.model_validate(merged)
|
hadalized/cache.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Application cache layer."""
|
|
2
|
+
|
|
3
|
+
import sqlite3
|
|
4
|
+
from typing import TYPE_CHECKING, Self
|
|
5
|
+
|
|
6
|
+
from loguru import logger
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from hadalized.options import Options
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Cache:
|
|
15
|
+
"""Caching layer."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, opts: Options | None = None):
|
|
18
|
+
"""Create a new instance.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
opts: Runtime options controlling location of cache, whether to
|
|
22
|
+
use it, etc.
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
self.opt = opts or Options()
|
|
26
|
+
self.cache_dir: Path = self.opt.cache_dir
|
|
27
|
+
self._db_file: Path = self.cache_dir / "builds.db"
|
|
28
|
+
if not self.opt.cache_in_memory:
|
|
29
|
+
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
30
|
+
self._conn: sqlite3.Connection
|
|
31
|
+
|
|
32
|
+
def _setup(self):
|
|
33
|
+
with self._conn:
|
|
34
|
+
self._conn.execute("""
|
|
35
|
+
CREATE TABLE IF NOT EXISTS
|
|
36
|
+
digests(path TEXT PRIMARY KEY, digest TEXT)""")
|
|
37
|
+
# self._conn.execute("""
|
|
38
|
+
# CREATE TABLE IF NOT EXISTS
|
|
39
|
+
# palettes(name TEXT PRIMARY KEY, data TEXT)""")
|
|
40
|
+
|
|
41
|
+
def connect(self) -> Self:
|
|
42
|
+
"""Connect to the underlying sqlite database.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
The connected instance.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
file = ":memory:" if self.opt.cache_in_memory else self._db_file
|
|
49
|
+
self._conn = sqlite3.connect(file)
|
|
50
|
+
self._setup()
|
|
51
|
+
return self
|
|
52
|
+
|
|
53
|
+
def close(self):
|
|
54
|
+
"""Close the database connection."""
|
|
55
|
+
self._conn.close()
|
|
56
|
+
|
|
57
|
+
def add(self, path: str | Path, digest: str):
|
|
58
|
+
"""Add a (path, hash hexdigest) to the database.
|
|
59
|
+
|
|
60
|
+
Used after a file is successfully generated to store a proxy for
|
|
61
|
+
the contents of the generated file.
|
|
62
|
+
"""
|
|
63
|
+
with self._conn:
|
|
64
|
+
self._conn.execute(
|
|
65
|
+
"""INSERT INTO digests VALUES(:path, :digest)
|
|
66
|
+
ON CONFLICT(path) DO UPDATE SET digest=:digest""",
|
|
67
|
+
{
|
|
68
|
+
"path": str(path),
|
|
69
|
+
"digest": digest,
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def delete(self, path: str | Path):
|
|
74
|
+
"""Remove a cache entry."""
|
|
75
|
+
with self._conn:
|
|
76
|
+
self._conn.execute(
|
|
77
|
+
"""DELETE FROM digests WHERE path == ?""",
|
|
78
|
+
[str(path)],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def get(self, path: str | Path) -> str | None:
|
|
82
|
+
"""Get a build digest for the input path.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
A hash (proxy) of the previously generated file or None if no
|
|
86
|
+
record is found.
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
with self._conn:
|
|
90
|
+
cur = self._conn.execute(
|
|
91
|
+
"""SELECT digest FROM digests WHERE path == ? LIMIT 1""",
|
|
92
|
+
[str(path)],
|
|
93
|
+
)
|
|
94
|
+
data = cur.fetchone()
|
|
95
|
+
return data[0] if isinstance(data, tuple) else None
|
|
96
|
+
|
|
97
|
+
def get_digests(self) -> dict[str, str]:
|
|
98
|
+
"""Obtain the contents of all cache build digests as a mapping.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Mapping of path -> hash digest for all paths in the cache.
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
with self._conn:
|
|
105
|
+
return dict(self._conn.execute("SELECT * from digests"))
|
|
106
|
+
|
|
107
|
+
def __enter__(self) -> Self:
|
|
108
|
+
"""Connect to the database.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
The connected instance.
|
|
112
|
+
|
|
113
|
+
"""
|
|
114
|
+
self.connect()
|
|
115
|
+
return self
|
|
116
|
+
|
|
117
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
118
|
+
"""Close the database connection."""
|
|
119
|
+
if exc_type is not None:
|
|
120
|
+
logger.error((exc_type, exc_value, traceback))
|
|
121
|
+
self.close()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Command line interface for hadalized theme builder."""
|
hadalized/cli/main.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""Application commands."""
|
|
2
|
+
|
|
3
|
+
from contextlib import suppress
|
|
4
|
+
from pathlib import Path # noqa
|
|
5
|
+
from shutil import rmtree
|
|
6
|
+
|
|
7
|
+
from cyclopts import App
|
|
8
|
+
|
|
9
|
+
from hadalized import homedirs
|
|
10
|
+
from hadalized.cache import Cache
|
|
11
|
+
from hadalized.config import Config, Options, load_config
|
|
12
|
+
from hadalized.writer import ThemeWriter
|
|
13
|
+
|
|
14
|
+
app = App()
|
|
15
|
+
cache_app = app.command(App(name="cache", help="Interact with the application cache."))
|
|
16
|
+
config_app = app.command(
|
|
17
|
+
App(name="config", help="Interact with the application config.")
|
|
18
|
+
)
|
|
19
|
+
palette_app = app.command(App(name="palette", help="Interact with palettes."))
|
|
20
|
+
state_app = app.command(
|
|
21
|
+
App(name="state", help="Interact with the application state, e.g., built files.")
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@app.command
|
|
26
|
+
def build(name: str | None = None, opt: Options | None = None):
|
|
27
|
+
"""Build application color themes files.
|
|
28
|
+
|
|
29
|
+
When no applications or palette is specified, themes will be built for all
|
|
30
|
+
application and palette pairs.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
name: Target application to build. Use ``--app`` to include multiple.
|
|
34
|
+
opt: Options.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
opt = opt or Options()
|
|
38
|
+
if name is not None:
|
|
39
|
+
opt |= Options(include_builds=[*opt.include_builds, name])
|
|
40
|
+
config = load_config(opt)
|
|
41
|
+
if config.dry_run:
|
|
42
|
+
print("DRY-RUN. No theme files will be generated or copied.")
|
|
43
|
+
with ThemeWriter(config) as writer:
|
|
44
|
+
writer.run()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# @config_app.command(name="info")
|
|
48
|
+
# def config_info(opt: Options | None = None):
|
|
49
|
+
# """Dispaly configuration info."""
|
|
50
|
+
# from rich import print_json
|
|
51
|
+
#
|
|
52
|
+
# config = load_config(opt)
|
|
53
|
+
# print_json(config.model_dump_json())
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@config_app.command(name="schema")
|
|
57
|
+
def config_schema():
|
|
58
|
+
"""Display configuration schema."""
|
|
59
|
+
import json
|
|
60
|
+
|
|
61
|
+
from rich import print_json
|
|
62
|
+
|
|
63
|
+
print_json(json.dumps(Config.model_json_schema()))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@config_app.command(name="init")
|
|
67
|
+
def config_init(opts: Options | None = None):
|
|
68
|
+
"""Populate application configuration toml file.
|
|
69
|
+
|
|
70
|
+
When `--output=stdout` the toml contents will be printed.
|
|
71
|
+
"""
|
|
72
|
+
import tomli_w as toml
|
|
73
|
+
|
|
74
|
+
config = load_config(opts)
|
|
75
|
+
if str(config.output_dir) == "stdout":
|
|
76
|
+
print(toml.dumps(config.model_dump(mode="json", exclude_none=True)))
|
|
77
|
+
return
|
|
78
|
+
output = config.output_dir or homedirs.config()
|
|
79
|
+
if output.suffix != ".toml":
|
|
80
|
+
output /= "config.toml"
|
|
81
|
+
|
|
82
|
+
output_exists = output.exists()
|
|
83
|
+
if output_exists and not config.quiet:
|
|
84
|
+
print(f"{output} already exists.")
|
|
85
|
+
if output_exists and not config.force:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
output.parent.mkdir(parents=True, exist_ok=True)
|
|
89
|
+
with output.open("wb") as fp:
|
|
90
|
+
if not config.quiet:
|
|
91
|
+
print(f"Creating {output}")
|
|
92
|
+
data = config.model_dump(mode="json", exclude_none=True)
|
|
93
|
+
if not config.dry_run:
|
|
94
|
+
toml.dump(data, fp)
|
|
95
|
+
# except TypeError as exc:
|
|
96
|
+
# print(f"Unable to write config file: {exc}")
|
|
97
|
+
# output.unlink()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@palette_app.command(name="parse")
|
|
101
|
+
def palette_parse(
|
|
102
|
+
name: str = "hadalized",
|
|
103
|
+
*,
|
|
104
|
+
gamut: str | None = None,
|
|
105
|
+
opt: Options | None = None,
|
|
106
|
+
):
|
|
107
|
+
"""Show information about a particular palette.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
name: Palette name or alias, e.g., "hadalized".
|
|
111
|
+
gamut: A specifed gamut to parse against. If not provided, the
|
|
112
|
+
gamut defined by the palette is used.
|
|
113
|
+
opt: Options
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
# TODO: Respect user config.
|
|
117
|
+
from rich import print_json
|
|
118
|
+
|
|
119
|
+
opt = opt or Options()
|
|
120
|
+
config = load_config(opt)
|
|
121
|
+
for item in [name, *config.include_palettes]:
|
|
122
|
+
palette = config.get_palette(item)
|
|
123
|
+
if gamut is not None:
|
|
124
|
+
palette = palette.replace(gamut=gamut)
|
|
125
|
+
print_json(palette.parse().model_dump_json())
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@cache_app.command(name="clean")
|
|
129
|
+
def cache_clean(opt: Options | None = None):
|
|
130
|
+
"""Clear the application cache."""
|
|
131
|
+
config = load_config(opt)
|
|
132
|
+
if config.dry_run and not config.quiet:
|
|
133
|
+
print("DRY-RUN: Cache files will not be deleted.")
|
|
134
|
+
if not config.quiet:
|
|
135
|
+
print(f"Clearing {config.cache_dir}")
|
|
136
|
+
if config.verbose:
|
|
137
|
+
files = "\n".join(str(x) for x in config.cache_dir.glob("**/*") if x.is_file())
|
|
138
|
+
print(files)
|
|
139
|
+
if not config.dry_run:
|
|
140
|
+
with suppress(FileNotFoundError):
|
|
141
|
+
rmtree(config.cache_dir)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@cache_app.command(name="dir")
|
|
145
|
+
def cache_dir(opt: Options | None = None):
|
|
146
|
+
"""Show the cache directory."""
|
|
147
|
+
config = load_config(opt)
|
|
148
|
+
print(config.cache_dir)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@cache_app.command(name="list", alias=["ls"])
|
|
152
|
+
def cache_list(opt: Options | None = None):
|
|
153
|
+
"""List the contents of the application cache."""
|
|
154
|
+
import json
|
|
155
|
+
|
|
156
|
+
from rich import print_json
|
|
157
|
+
|
|
158
|
+
config = load_config(opt)
|
|
159
|
+
|
|
160
|
+
with Cache(config.opt) as cache:
|
|
161
|
+
print_json(json.dumps(cache.get_digests()))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@state_app.command(name="dir")
|
|
165
|
+
def state_dir(opt: Options | None = None):
|
|
166
|
+
"""Show the applicate state directory."""
|
|
167
|
+
config = load_config(opt)
|
|
168
|
+
print(config.opt.state_dir)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@state_app.command(name="clean")
|
|
172
|
+
def state_clean(opt: Options | None = None):
|
|
173
|
+
"""Clear application state files such as built themes."""
|
|
174
|
+
config = load_config(opt)
|
|
175
|
+
if config.dry_run and not config.quiet:
|
|
176
|
+
print("DRY-RUN. No state files will be deleted.")
|
|
177
|
+
if not config.quiet:
|
|
178
|
+
# import json
|
|
179
|
+
#
|
|
180
|
+
# from rich import print_json
|
|
181
|
+
|
|
182
|
+
print(f"Clearing {config.state_dir}")
|
|
183
|
+
files = "\n".join(str(x) for x in config.state_dir.glob("**/*") if x.is_file())
|
|
184
|
+
if files:
|
|
185
|
+
print(files)
|
|
186
|
+
# print_json(json.dumps(files))
|
|
187
|
+
if not config.dry_run:
|
|
188
|
+
with suppress(FileNotFoundError):
|
|
189
|
+
rmtree(config.state_dir)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@state_app.command(name="list", alias=["ls"])
|
|
193
|
+
def state_list(opt: Options | None = None):
|
|
194
|
+
"""List application state files."""
|
|
195
|
+
config = load_config(opt)
|
|
196
|
+
files = "\n".join(str(x) for x in config.state_dir.glob("**/*") if x.is_file())
|
|
197
|
+
print(files)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@app.command
|
|
201
|
+
def clean(opt: Options | None = None):
|
|
202
|
+
"""Clean cache and state files."""
|
|
203
|
+
cache_clean(opt)
|
|
204
|
+
state_clean(opt)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# @app.command
|
|
208
|
+
# def debug(opt: Options | None = None):
|
|
209
|
+
# """Debug things."""
|
|
210
|
+
# from rich import print_json
|
|
211
|
+
#
|
|
212
|
+
# config = load_config(opt)
|
|
213
|
+
# print(f"{config.cache_dir=}")
|
|
214
|
+
# print(f"{config.dry_run=}")
|
|
215
|
+
# print(f"{opt=}")
|
|
216
|
+
# if opt is not None:
|
|
217
|
+
# print(f"{opt.model_fields_set=}")
|
|
218
|
+
# print("config.opt")
|
|
219
|
+
# print_json(config.opt.model_dump_json())
|
|
220
|
+
# print(f"{config.model_fields_set=}")
|
|
221
|
+
# print(f"{config.opt.model_fields_set=}")
|