add py execution support to templates, loosen generate matches
This commit is contained in:
parent
017f1c5b1c
commit
2553bc14af
19
README.md
19
README.md
@ -19,8 +19,9 @@ used when populating config file templates. Specifically, in this example, invok
|
|||||||
are responsive to `prefers-color-scheme`)
|
are responsive to `prefers-color-scheme`)
|
||||||
- **kitty**: theme template is re-generated using the specified palette, and `kitty`
|
- **kitty**: theme template is re-generated using the specified palette, and `kitty`
|
||||||
processes are sent a message to live-reload the new config file
|
processes are sent a message to live-reload the new config file
|
||||||
- **neovim**: a `vim` theme file is generated from the chosen palette, and running
|
- **neovim**: a `vim` theme file (along with a statusline theme) is generated from the
|
||||||
instances of `neovim` are sent a message to re-source this theme
|
chosen palette, and running instances of `neovim` are sent a message to re-source this
|
||||||
|
theme (via `nvim --remote-send`)
|
||||||
- **waybar**: bar styles are updated to match the mode setting
|
- **waybar**: bar styles are updated to match the mode setting
|
||||||
- **sway**: the background color and window borders are dynamically set to base palette
|
- **sway**: the background color and window borders are dynamically set to base palette
|
||||||
colors, and `swaymsg reload` is called
|
colors, and `swaymsg reload` is called
|
||||||
@ -73,13 +74,25 @@ You can also install via `pip`, or clone and install locally.
|
|||||||
all registered apps.
|
all registered apps.
|
||||||
* `-m --mode`: preferred lightness mode/scheme, either `light`, `dark`, `any`, or
|
* `-m --mode`: preferred lightness mode/scheme, either `light`, `dark`, `any`, or
|
||||||
`none`.
|
`none`.
|
||||||
* `-s --style`: style indicate, often the name of a color palette, capturing thematic
|
* `-s --style`: style indicator, often the name of a color palette, capturing thematic
|
||||||
details in a config file to be matched. `any` or `none` are reserved keywords (see
|
details in a config file to be matched. `any` or `none` are reserved keywords (see
|
||||||
below).
|
below).
|
||||||
* `-T --template-vars`: additional groups to use when populating templates, in the form
|
* `-T --template-vars`: additional groups to use when populating templates, in the form
|
||||||
`<group>=<value>`, where `<group>` is a template group with a folder
|
`<group>=<value>`, where `<group>` is a template group with a folder
|
||||||
`$CONFIG_HOME/groups/<group>/` and `<value>` should correspond to a TOML file in this
|
`$CONFIG_HOME/groups/<group>/` and `<value>` should correspond to a TOML file in this
|
||||||
folder (i.e., `<value>.toml`).
|
folder (i.e., `<value>.toml`).
|
||||||
|
- `symconf generate` is a subcommand that can be used for batch generation of config
|
||||||
|
files. It accepts the same arguments as `symconf config`, but rather than selecting the
|
||||||
|
best match to be used for the system setting, all matching templates are generated.
|
||||||
|
There is one additional required argument:
|
||||||
|
* `-o --output-dir`: the directory under which generated config files should be written.
|
||||||
|
App-specific subdirectories are created to house config files for each provided app.
|
||||||
|
- `symconf install`: runs install scripts for matching apps that specify one
|
||||||
|
* `-a --apps`: comma-separate list of registered apps, or `"*"` (default) to consider
|
||||||
|
all registered apps.
|
||||||
|
- `symconf update`: runs update scripts for matching apps that specify one
|
||||||
|
* `-a --apps`: comma-separate list of registered apps, or `"*"` (default) to consider
|
||||||
|
all registered apps.
|
||||||
|
|
||||||
The keywords `any` and `none` can be used when specifying `--mode`, `--style`, or as a
|
The keywords `any` and `none` can be used when specifying `--mode`, `--style`, or as a
|
||||||
value in `--template-vars` (and we refer to each of these variables as _factors_ that help
|
value in `--template-vars` (and we refer to each of these variables as _factors_ that help
|
||||||
|
@ -3,22 +3,29 @@
|
|||||||
{ref}`modindex`
|
{ref}`modindex`
|
||||||
{ref}`search`
|
{ref}`search`
|
||||||
|
|
||||||
|
## Top-level module overview
|
||||||
|
|
||||||
```{eval-rst}
|
```{eval-rst}
|
||||||
.. autosummary::
|
.. autosummary::
|
||||||
:nosignatures:
|
:nosignatures:
|
||||||
|
:recursive:
|
||||||
|
|
||||||
# list modules here for quick links
|
symconf.config
|
||||||
|
symconf.template
|
||||||
|
symconf.matching
|
||||||
|
symconf.reader
|
||||||
|
symconf.runner
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Auto-reference contents
|
||||||
```{toctree}
|
```{toctree}
|
||||||
:maxdepth: 3
|
:maxdepth: 3
|
||||||
:caption: Autoref
|
|
||||||
|
|
||||||
_autoref/symconf.rst
|
_autoref/symconf.rst
|
||||||
```
|
```
|
||||||
|
|
||||||
```{toctree}
|
```{toctree}
|
||||||
:maxdepth: 3
|
:maxdepth: 2
|
||||||
:caption: Contents
|
:caption: Contents
|
||||||
|
|
||||||
reference/configuring
|
reference/configuring
|
||||||
@ -28,4 +35,7 @@ reference/documentation/index
|
|||||||
```
|
```
|
||||||
|
|
||||||
```{include} ../README.md
|
```{include} ../README.md
|
||||||
|
:relative-docs: docs/
|
||||||
|
:relative-images:
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ def add_config_subparser(subparsers):
|
|||||||
apps=args.apps,
|
apps=args.apps,
|
||||||
scheme=args.mode,
|
scheme=args.mode,
|
||||||
style=args.style,
|
style=args.style,
|
||||||
|
**args.template_vars
|
||||||
)
|
)
|
||||||
|
|
||||||
parser = subparsers.add_parser(
|
parser = subparsers.add_parser(
|
||||||
@ -81,6 +82,7 @@ def add_config_subparser(subparsers):
|
|||||||
'-T', '--template-vars',
|
'-T', '--template-vars',
|
||||||
required = False,
|
required = False,
|
||||||
nargs='+',
|
nargs='+',
|
||||||
|
default = {},
|
||||||
action=util.KVPair,
|
action=util.KVPair,
|
||||||
help='Groups to use when populating templates, in the form group=value'
|
help='Groups to use when populating templates, in the form group=value'
|
||||||
)
|
)
|
||||||
@ -90,8 +92,11 @@ def add_generate_subparser(subparsers):
|
|||||||
def generate_apps(args):
|
def generate_apps(args):
|
||||||
cm = ConfigManager(args.config_dir)
|
cm = ConfigManager(args.config_dir)
|
||||||
cm.generate_app_templates(
|
cm.generate_app_templates(
|
||||||
gen_dir=args.gen_dir,
|
gen_dir=args.output_dir,
|
||||||
apps=args.apps,
|
apps=args.apps,
|
||||||
|
scheme=args.mode,
|
||||||
|
style=args.style,
|
||||||
|
**args.template_vars
|
||||||
)
|
)
|
||||||
|
|
||||||
parser = subparsers.add_parser(
|
parser = subparsers.add_parser(
|
||||||
@ -99,11 +104,24 @@ def add_generate_subparser(subparsers):
|
|||||||
description='Generate all template config files for specified apps'
|
description='Generate all template config files for specified apps'
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-g', '--gen-dir',
|
'-o', '--output-dir',
|
||||||
required = True,
|
required = True,
|
||||||
type = util.absolute_path,
|
type = util.absolute_path,
|
||||||
help = 'Path to write generated template files'
|
help = 'Path to write generated template files'
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'-s', '--style',
|
||||||
|
required = False,
|
||||||
|
default = 'any',
|
||||||
|
help = 'Style indicator (often a color palette) capturing thematic details in '
|
||||||
|
'a config file'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'-m', '--mode',
|
||||||
|
required = False,
|
||||||
|
default = "any",
|
||||||
|
help = 'Preferred lightness mode/scheme, either "light," "dark," "any," or "none."'
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'-a', '--apps',
|
'-a', '--apps',
|
||||||
required = False,
|
required = False,
|
||||||
@ -112,6 +130,14 @@ def add_generate_subparser(subparsers):
|
|||||||
help = 'Application target for theme. App must be present in the registry. ' \
|
help = 'Application target for theme. App must be present in the registry. ' \
|
||||||
+ 'Use "*" to apply to all registered apps'
|
+ 'Use "*" to apply to all registered apps'
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'-T', '--template-vars',
|
||||||
|
required = False,
|
||||||
|
nargs = '+',
|
||||||
|
default = {},
|
||||||
|
action = util.KVPair,
|
||||||
|
help = 'Groups to use when populating templates, in the form group=value'
|
||||||
|
)
|
||||||
parser.set_defaults(func=generate_apps)
|
parser.set_defaults(func=generate_apps)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
'''
|
'''
|
||||||
Get the config map for a provided app.
|
Primary config management abstractions
|
||||||
|
|
||||||
The config map is a dict mapping from config file **path names** to their absolute
|
The config map is a dict mapping from config file **path names** to their absolute
|
||||||
path locations. That is,
|
path locations. That is,
|
||||||
|
|
||||||
```sh
|
.. code-block:: sh
|
||||||
<config_path_name> -> <config_dir>/apps/<app_name>/<subdir>/<palette>-<scheme>.<config_path_name>
|
|
||||||
```
|
<config_path_name> -> <config_dir>/apps/<app_name>/<subdir>/<palette>-<scheme>.<config_path_name>
|
||||||
|
|
||||||
For example,
|
For example,
|
||||||
|
|
||||||
```
|
.. code-block:: sh
|
||||||
palette1-light.conf.ini -> ~/.config/symconf/apps/user/palette1-light.conf.ini
|
|
||||||
palette2-dark.app.conf -> ~/.config/symconf/apps/generated/palette2-dark.app.conf
|
palette1-light.conf.ini -> ~/.config/symconf/apps/user/palette1-light.conf.ini
|
||||||
```
|
palette2-dark.app.conf -> ~/.config/symconf/apps/generated/palette2-dark.app.conf
|
||||||
|
|
||||||
This ensures we have unique config names pointing to appropriate locations (which
|
This ensures we have unique config names pointing to appropriate locations (which
|
||||||
is mostly important when the same config file names are present across ``user``
|
is mostly important when the same config file names are present across ``user``
|
||||||
@ -220,16 +220,16 @@ class ConfigManager:
|
|||||||
files just the same as we do for non-template config files. That is, we will look
|
files just the same as we do for non-template config files. That is, we will look
|
||||||
for files of the format
|
for files of the format
|
||||||
|
|
||||||
```sh
|
.. code-block:: sh
|
||||||
<style>-<scheme>.toml
|
|
||||||
```
|
<style>-<scheme>.toml
|
||||||
|
|
||||||
The only difference is that, while ``style`` can still include arbitrary style
|
The only difference is that, while ``style`` can still include arbitrary style
|
||||||
variants, it *must* have the form
|
variants, it *must* have the form
|
||||||
|
|
||||||
```sh
|
.. code-block:: sh
|
||||||
<variant-1>-...-<variant-N>-<palette>
|
|
||||||
```
|
<variant-1>-...-<variant-N>-<palette>
|
||||||
|
|
||||||
if you want to match a ``palette`` template. Palettes are like regular template
|
if you want to match a ``palette`` template. Palettes are like regular template
|
||||||
groups, and should be placed in their own template folder. But when applying those
|
groups, and should be placed in their own template folder. But when applying those
|
||||||
@ -346,7 +346,11 @@ class ConfigManager:
|
|||||||
|
|
||||||
return template_dict, relaxed_theme_matches
|
return template_dict, relaxed_theme_matches
|
||||||
|
|
||||||
def _prepare_all_templates(self) -> dict[str, dict]:
|
def _prepare_all_templates(
|
||||||
|
self,
|
||||||
|
scheme = 'any',
|
||||||
|
style = 'any',
|
||||||
|
) -> dict[str, dict]:
|
||||||
palette_map = {}
|
palette_map = {}
|
||||||
palette_group_dir = Path(self.group_dir, 'palette')
|
palette_group_dir = Path(self.group_dir, 'palette')
|
||||||
if palette_group_dir.exists():
|
if palette_group_dir.exists():
|
||||||
@ -358,30 +362,37 @@ class ConfigManager:
|
|||||||
palette_base.append(palette_map['none'])
|
palette_base.append(palette_map['none'])
|
||||||
|
|
||||||
# then palette-scheme groups (require 2-combo logic)
|
# then palette-scheme groups (require 2-combo logic)
|
||||||
theme_map = {}
|
theme_matches = []
|
||||||
theme_group_dir = Path(self.group_dir, 'theme')
|
theme_group_dir = Path(self.group_dir, 'theme')
|
||||||
if theme_group_dir.exists():
|
if theme_group_dir.exists():
|
||||||
for theme_toml in theme_group_dir.iterdir():
|
theme_matches = self.matcher.match_paths(
|
||||||
fp = FilePart(theme_toml)
|
theme_group_dir.iterdir(), # match files in groups/theme/
|
||||||
|
self.matcher.prefix_order( # reg non-template order
|
||||||
|
scheme, style, strict=True # set strict=True to ignore "nones"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
theme_matches = self.matcher.match_paths(
|
theme_map = {}
|
||||||
theme_group_dir.iterdir(), # match files in groups/theme/
|
for fp in theme_matches:
|
||||||
self.matcher.prefix_order(fp.scheme, fp.style) # reg non-template order
|
# still look through whole theme dir here (eg to match nones)
|
||||||
)
|
theme_matches = self.matcher.match_paths(
|
||||||
relaxed_theme_matches = self.matcher.relaxed_match(theme_matches)
|
theme_group_dir.iterdir(), # match files in groups/theme/
|
||||||
|
self.matcher.prefix_order(fp.scheme, fp.style) # reg non-template order
|
||||||
|
)
|
||||||
|
relaxed_theme_matches = self.matcher.relaxed_match(theme_matches)
|
||||||
|
|
||||||
palette = fp.style.split('-')[-1]
|
palette = fp.style.split('-')[-1]
|
||||||
palette_paths = [*palette_base]
|
palette_paths = [*palette_base]
|
||||||
if palette in palette_map:
|
if palette in palette_map:
|
||||||
palette_paths.append(palette_map[palette])
|
palette_paths.append(palette_map[palette])
|
||||||
|
|
||||||
theme_dict = {}
|
theme_dict = {}
|
||||||
palette_dict = TOMLTemplate.stack_toml(palette_paths)
|
palette_dict = TOMLTemplate.stack_toml(palette_paths)
|
||||||
for file_part in relaxed_theme_matches:
|
for file_part in relaxed_theme_matches:
|
||||||
toml_dict = TOMLTemplate(file_part.path).fill(palette_dict)
|
toml_dict = TOMLTemplate(file_part.path).fill(palette_dict)
|
||||||
theme_dict = util.deep_update(theme_dict, toml_dict)
|
theme_dict = util.deep_update(theme_dict, toml_dict)
|
||||||
|
|
||||||
theme_map[fp.path.stem] = {'theme': theme_dict}
|
theme_map[fp.path.stem] = {'theme': theme_dict}
|
||||||
|
|
||||||
return theme_map
|
return theme_map
|
||||||
|
|
||||||
@ -406,7 +417,7 @@ class ConfigManager:
|
|||||||
``none-<scheme>`` and ``<style>-none`` are both available, in which case the latter
|
``none-<scheme>`` and ``<style>-none`` are both available, in which case the latter
|
||||||
will overwrite the former).
|
will overwrite the former).
|
||||||
|
|
||||||
.. admonition: Edge cases
|
.. admonition:: Edge cases
|
||||||
|
|
||||||
There are a few quirks to this matching scheme that yield potentially
|
There are a few quirks to this matching scheme that yield potentially
|
||||||
unintuitive results. As a recap:
|
unintuitive results. As a recap:
|
||||||
@ -515,9 +526,9 @@ class ConfigManager:
|
|||||||
|
|
||||||
Scripts need to be placed in
|
Scripts need to be placed in
|
||||||
|
|
||||||
```sh
|
.. code-block:: sh
|
||||||
<config_dir>/apps/<app_name>/call/<style>-<scheme>.sh
|
|
||||||
```
|
<config_dir>/apps/<app_name>/call/<style>-<scheme>.sh
|
||||||
|
|
||||||
and are matched using the same heuristic employed by config file symlinking
|
and are matched using the same heuristic employed by config file symlinking
|
||||||
procedure (see ``get_matching_configs()``), albeit with a forced ``prefix_order``,
|
procedure (see ``get_matching_configs()``), albeit with a forced ``prefix_order``,
|
||||||
@ -808,6 +819,9 @@ class ConfigManager:
|
|||||||
self,
|
self,
|
||||||
gen_dir : str | Path,
|
gen_dir : str | Path,
|
||||||
apps : str | list[str] = '*',
|
apps : str | list[str] = '*',
|
||||||
|
scheme : str = 'any',
|
||||||
|
style : str = 'any',
|
||||||
|
**kw_groups,
|
||||||
):
|
):
|
||||||
if apps == '*':
|
if apps == '*':
|
||||||
app_list = list(self.app_registry.keys())
|
app_list = list(self.app_registry.keys())
|
||||||
@ -823,7 +837,7 @@ class ConfigManager:
|
|||||||
print(f'> Writing templates...')
|
print(f'> Writing templates...')
|
||||||
|
|
||||||
gen_dir = util.absolute_path(gen_dir)
|
gen_dir = util.absolute_path(gen_dir)
|
||||||
theme_map = self._prepare_all_templates()
|
theme_map = self._prepare_all_templates(scheme, style)
|
||||||
|
|
||||||
for app_name in app_list:
|
for app_name in app_list:
|
||||||
app_template_dir = Path(self.apps_dir, app_name, 'templates')
|
app_template_dir = Path(self.apps_dir, app_name, 'templates')
|
||||||
@ -831,6 +845,12 @@ class ConfigManager:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
app_template_files = list(app_template_dir.iterdir())
|
app_template_files = list(app_template_dir.iterdir())
|
||||||
|
self.get_matching_templates(
|
||||||
|
app_name,
|
||||||
|
scheme=scheme,
|
||||||
|
style=style,
|
||||||
|
**kw_groups
|
||||||
|
)
|
||||||
|
|
||||||
num_temps = len(app_template_files)
|
num_temps = len(app_template_files)
|
||||||
num_themes = len(theme_map)
|
num_themes = len(theme_map)
|
||||||
@ -855,5 +875,5 @@ class ConfigManager:
|
|||||||
|
|
||||||
print(
|
print(
|
||||||
color_text("│", Fore.BLUE),
|
color_text("│", Fore.BLUE),
|
||||||
f'> generating "{tgt_template_path.name}"'
|
f'> generating "{tgt_template_path.name}"'
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
'''
|
'''
|
||||||
Top-level definitions
|
Generic combinatorial name-matching subsystem
|
||||||
|
|
||||||
Config files are expected to have names matching the following spec:
|
Config files are expected to have names matching the following spec:
|
||||||
|
|
||||||
<style>-<scheme>.<config_pathname>
|
.. code-block:: sh
|
||||||
|
|
||||||
|
<style>-<scheme>.<config_pathname>
|
||||||
|
|
||||||
- ``config_pathname``: refers to a concrete filename, typically that which is expected by
|
- ``config_pathname``: refers to a concrete filename, typically that which is expected by
|
||||||
the target app (e.g., ``kitty.conf``). In the context of ``config_map`` in the registry,
|
the target app (e.g., ``kitty.conf``). In the context of ``config_map`` in the registry,
|
||||||
@ -15,17 +17,17 @@ Config files are expected to have names matching the following spec:
|
|||||||
|
|
||||||
For example
|
For example
|
||||||
|
|
||||||
```sh
|
.. code-block:: sh
|
||||||
soft-gruvbox-dark.kitty.conf
|
|
||||||
```
|
soft-gruvbox-dark.kitty.conf
|
||||||
|
|
||||||
gets mapped to
|
gets mapped to
|
||||||
|
|
||||||
```sh
|
.. code-block:: sh
|
||||||
style -> "soft-gruvbox"
|
|
||||||
scheme -> "dark"
|
style -> "soft-gruvbox"
|
||||||
pathname -> "kitty.conf"
|
scheme -> "dark"
|
||||||
```
|
pathname -> "kitty.conf"
|
||||||
'''
|
'''
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -64,9 +66,9 @@ class Matcher:
|
|||||||
|
|
||||||
Pathnames should be of the format
|
Pathnames should be of the format
|
||||||
|
|
||||||
```sh
|
.. code-block:: sh
|
||||||
<style>-<scheme>.<config_pathname>
|
|
||||||
```
|
<style>-<scheme>.<config_pathname>
|
||||||
|
|
||||||
where ``style`` is typically itself of the form ``<variant>-<palette>``.
|
where ``style`` is typically itself of the form ``<variant>-<palette>``.
|
||||||
'''
|
'''
|
||||||
@ -151,12 +153,12 @@ class Matcher:
|
|||||||
"consistent" with some user input (and is computed external to this method). For
|
"consistent" with some user input (and is computed external to this method). For
|
||||||
example, it could be
|
example, it could be
|
||||||
|
|
||||||
```py
|
.. code-block:: python
|
||||||
[
|
|
||||||
('none', 'none')
|
[
|
||||||
('none', 'dark')
|
('none', 'none')
|
||||||
]
|
('none', 'dark')
|
||||||
```
|
]
|
||||||
|
|
||||||
indicating that either ``none-none.<config>`` or ``none-dark.<config>`` would be
|
indicating that either ``none-none.<config>`` or ``none-dark.<config>`` would be
|
||||||
considered matching pathnames, with the latter being preferred.
|
considered matching pathnames, with the latter being preferred.
|
||||||
@ -166,7 +168,7 @@ class Matcher:
|
|||||||
will be available, so we consider each file for each of the prefixes, and take the
|
will be available, so we consider each file for each of the prefixes, and take the
|
||||||
latest/best match for each unique config pathname (allowing for a "soft" match).
|
latest/best match for each unique config pathname (allowing for a "soft" match).
|
||||||
|
|
||||||
.. admonition: Checking for matches
|
.. admonition:: Checking for matches
|
||||||
|
|
||||||
When thinking about how best to structure this method, it initially felt like
|
When thinking about how best to structure this method, it initially felt like
|
||||||
indexing factors of the FileParts would make the most sense, preventing the
|
indexing factors of the FileParts would make the most sense, preventing the
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
'''
|
||||||
|
Simplified management for nested dictionaries
|
||||||
|
'''
|
||||||
import copy
|
import copy
|
||||||
import pprint
|
import pprint
|
||||||
import tomllib
|
import tomllib
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
'''
|
||||||
|
Handle job/script execution
|
||||||
|
'''
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
'''
|
||||||
|
Support for basic config templates
|
||||||
|
'''
|
||||||
import re
|
import re
|
||||||
import tomllib
|
import tomllib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -10,10 +13,12 @@ class Template:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
template_str : str,
|
template_str : str,
|
||||||
pattern : str = r'f{{(\S+?)}}',
|
key_pattern : str = r'f{{(\S+?)}}',
|
||||||
|
exe_pattern : str = r'x{{(.*)}}',
|
||||||
):
|
):
|
||||||
self.template_str = template_str
|
self.template_str = template_str
|
||||||
self.pattern = pattern
|
self.key_pattern = key_pattern
|
||||||
|
self.exe_pattern = exe_pattern
|
||||||
|
|
||||||
def fill(
|
def fill(
|
||||||
self,
|
self,
|
||||||
@ -21,32 +26,66 @@ class Template:
|
|||||||
) -> str:
|
) -> str:
|
||||||
dr = DictReader.from_dict(template_dict)
|
dr = DictReader.from_dict(template_dict)
|
||||||
|
|
||||||
return re.sub(
|
exe_filled = re.sub(
|
||||||
self.pattern,
|
self.exe_pattern,
|
||||||
lambda m: str(dr.get(m.group(1))),
|
lambda m: self._exe_fill(m, dr),
|
||||||
self.template_str
|
self.template_str
|
||||||
)
|
)
|
||||||
|
|
||||||
|
key_filled = re.sub(
|
||||||
|
self.key_pattern,
|
||||||
|
lambda m: self._key_fill(m, dr),
|
||||||
|
exe_filled
|
||||||
|
)
|
||||||
|
|
||||||
|
return key_filled
|
||||||
|
|
||||||
|
def _key_fill(
|
||||||
|
self,
|
||||||
|
match,
|
||||||
|
dict_reader: DictReader,
|
||||||
|
) -> str:
|
||||||
|
key = match.group(1)
|
||||||
|
|
||||||
|
return str(dict_reader.get(key))
|
||||||
|
|
||||||
|
def _exe_fill(
|
||||||
|
self,
|
||||||
|
match,
|
||||||
|
dict_reader: DictReader,
|
||||||
|
) -> str:
|
||||||
|
key_fill = re.sub(
|
||||||
|
self.key_pattern,
|
||||||
|
lambda m: f'"{self._key_fill(m, dict_reader)}"',
|
||||||
|
match.group(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
return str(eval(key_fill))
|
||||||
|
|
||||||
class FileTemplate(Template):
|
class FileTemplate(Template):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
path : Path,
|
path: Path,
|
||||||
pattern : str = r'f{{(\S+)}}',
|
key_pattern: str = r'f{{(\S+?)}}',
|
||||||
|
exe_pattern: str = r'x{{(.*)}}',
|
||||||
):
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
path.open('r').read(),
|
path.open('r').read(),
|
||||||
pattern=pattern
|
key_pattern=key_pattern,
|
||||||
|
exe_pattern=exe_pattern,
|
||||||
)
|
)
|
||||||
|
|
||||||
class TOMLTemplate(FileTemplate):
|
class TOMLTemplate(FileTemplate):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
toml_path : Path,
|
toml_path: Path,
|
||||||
pattern : str = r'f{{(\S+)}}',
|
key_pattern: str = r'f{{(\S+?)}}',
|
||||||
|
exe_pattern: str = r'x{{(.*)}}',
|
||||||
):
|
):
|
||||||
super().__init__(
|
super().__init__(
|
||||||
toml_path,
|
toml_path,
|
||||||
pattern=pattern
|
key_pattern=key_pattern,
|
||||||
|
exe_pattern=exe_pattern,
|
||||||
)
|
)
|
||||||
|
|
||||||
def fill(
|
def fill(
|
||||||
|
@ -14,7 +14,7 @@ def color_text(text, *colorama_args):
|
|||||||
Note: we attempt to preserve expected nested behavior by only resetting the groups
|
Note: we attempt to preserve expected nested behavior by only resetting the groups
|
||||||
(Fore, Back, Style) affected the styles passed in. This works when an outer call is
|
(Fore, Back, Style) affected the styles passed in. This works when an outer call is
|
||||||
changing styles in one group, and an inner call is changing styles in another, but
|
changing styles in one group, and an inner call is changing styles in another, but
|
||||||
_not_ when affected groups overlap.
|
*not* when affected groups overlap.
|
||||||
|
|
||||||
For example, if an outer call is setting the foreground color (e.g., ``Fore.GREEN``),
|
For example, if an outer call is setting the foreground color (e.g., ``Fore.GREEN``),
|
||||||
nested calls on the text being passed into the function can modify and reset the
|
nested calls on the text being passed into the function can modify and reset the
|
||||||
|
Loading…
Reference in New Issue
Block a user