improve type hint coverage, make ruff format compliant
This commit is contained in:
parent
2b0702fe36
commit
4d707b97e4
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@ __pycache__/
|
||||
.ipynb_checkpoints/
|
||||
.pytest_cache/
|
||||
.python-version
|
||||
.venv/
|
||||
|
||||
# vendor and build files
|
||||
dist/
|
||||
|
@ -60,7 +60,7 @@ for managing Python packages meant to be used as CLI programs. With `uv` on your
|
||||
you can install with
|
||||
|
||||
```sh
|
||||
uv too install symconf
|
||||
uv tool install symconf
|
||||
```
|
||||
|
||||
Alternatively, you can use `pipx` to similar effect:
|
||||
|
40
docs/conf.py
40
docs/conf.py
@ -3,43 +3,47 @@
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# -- Project information ------------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'symconf'
|
||||
copyright = '2024, Sam Griesemer'
|
||||
author = 'Sam Griesemer'
|
||||
project = "symconf"
|
||||
copyright = "2025, Sam Griesemer"
|
||||
author = "Sam Griesemer"
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# -- General configuration ----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.autosummary", # enables a directive to be specified manually that gathers
|
||||
# module/object summary details in a table
|
||||
"sphinx.ext.viewcode", # allow viewing source in the HTML pages
|
||||
"myst_parser", # only really applies to manual docs; docstrings still need RST-like
|
||||
"sphinx.ext.napoleon", # enables Google-style docstring formats
|
||||
"sphinx_autodoc_typehints", # external extension that allows arg types to be inferred by type hints
|
||||
# enables a directive to be specified manually that gathers module/object
|
||||
# summary details in a table
|
||||
"sphinx.ext.autosummary",
|
||||
# allow viewing source in the HTML pages
|
||||
"sphinx.ext.viewcode",
|
||||
# only really applies to manual docs; docstrings still need RST-like
|
||||
"myst_parser",
|
||||
# enables Google-style docstring formats
|
||||
"sphinx.ext.napoleon",
|
||||
# external extension that allows arg types to be inferred by type hints
|
||||
"sphinx_autodoc_typehints",
|
||||
]
|
||||
autosummary_generate = True
|
||||
autosummary_imported_members = True
|
||||
|
||||
# include __init__ definitions in autodoc
|
||||
autodoc_default_options = {
|
||||
'special-members': '__init__',
|
||||
"special-members": "__init__",
|
||||
}
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
templates_path = ["_templates"]
|
||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||
|
||||
html_theme = 'furo'
|
||||
html_static_path = ['_static']
|
||||
html_theme = "furo"
|
||||
html_static_path = ["_static"]
|
||||
# html_sidebars = {
|
||||
# '**': ['/modules.html'],
|
||||
# }
|
||||
|
||||
|
@ -1,27 +1,26 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel", "setuptools-git-versioning>=2.0,<3"]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools-git-versioning]
|
||||
enabled = true
|
||||
|
||||
[project]
|
||||
name = "symconf"
|
||||
version = "0.8.2"
|
||||
description = "Local app configuration manager"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dynamic = ["version"]
|
||||
#license = {file = "LICENSE"}
|
||||
authors = [
|
||||
{ name="Sam Griesemer", email="samgriesemer+git@gmail.com" },
|
||||
]
|
||||
keywords = ["config"]
|
||||
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
keywords = ["tempate-engine", "theme-switcher", "configuration-files"]
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python",
|
||||
"Operating System :: OS Independent",
|
||||
"Development Status :: 3 - Alpha",
|
||||
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
]
|
||||
dependencies = [
|
||||
"pyxdg",
|
||||
@ -32,15 +31,16 @@ dependencies = [
|
||||
symconf = "symconf.__main__:main"
|
||||
|
||||
[project.optional-dependencies]
|
||||
tests = ["pytest"]
|
||||
docs = [
|
||||
doc = [
|
||||
"sphinx",
|
||||
"sphinx-togglebutton",
|
||||
"sphinx-autodoc-typehints",
|
||||
"furo",
|
||||
"myst-parser",
|
||||
]
|
||||
build = ["build", "twine"]
|
||||
dev = [
|
||||
"pytest"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://doc.olog.io/symconf"
|
||||
@ -50,3 +50,23 @@ Issues = "https://git.olog.io/olog/symconf/issues"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["symconf*"] # pattern to match package names
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 79
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["ANN", "E", "F", "UP", "B", "SIM", "I", "C4", "PERF"]
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
length-sort = true
|
||||
order-by-type = false
|
||||
force-sort-within-sections = false
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/**" = ["S101"]
|
||||
"**/__init__.py" = ["F401"]
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
docstring-code-format = true
|
||||
|
@ -1,16 +1,10 @@
|
||||
from symconf.runner import Runner
|
||||
from symconf.reader import DictReader
|
||||
from importlib.metadata import version
|
||||
|
||||
from symconf import util, config, reader, matching, template
|
||||
from symconf.config import ConfigManager
|
||||
from symconf.reader import DictReader
|
||||
from symconf.runner import Runner
|
||||
from symconf.matching import Matcher, FilePart
|
||||
from symconf.template import Template, FileTemplate, TOMLTemplate
|
||||
|
||||
from symconf import config
|
||||
from symconf import matching
|
||||
from symconf import reader
|
||||
from symconf import template
|
||||
from symconf import util
|
||||
|
||||
from importlib.metadata import version
|
||||
|
||||
|
||||
__version__ = version('symconf')
|
||||
__version__ = version("symconf")
|
||||
|
@ -1,179 +1,214 @@
|
||||
import argparse
|
||||
from importlib.metadata import version
|
||||
from argparse import Namespace, ArgumentParser
|
||||
|
||||
from symconf import util, __version__
|
||||
from symconf.config import ConfigManager
|
||||
|
||||
|
||||
def add_install_subparser(subparsers):
|
||||
def install_apps(args):
|
||||
def add_install_subparser(subparsers: ArgumentParser) -> None:
|
||||
def install_apps(args: Namespace) -> None:
|
||||
cm = ConfigManager(args.config_dir)
|
||||
cm.install_apps(apps=args.apps)
|
||||
|
||||
parser = subparsers.add_parser(
|
||||
'install',
|
||||
description='Run install scripts for registered applications.'
|
||||
"install",
|
||||
description="Run install scripts for registered applications.",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-a', '--apps',
|
||||
"-a",
|
||||
"--apps",
|
||||
required=False,
|
||||
default="*",
|
||||
type = lambda s: s.split(',') if s != '*' else s,
|
||||
help = 'Application target for theme. App must be present in the registry. ' \
|
||||
+ 'Use "*" to apply to all registered apps'
|
||||
type=lambda s: s.split(",") if s != "*" else s,
|
||||
help=(
|
||||
"Application target for theme. App must be present in the "
|
||||
'registry. Use "*" to apply to all registered apps'
|
||||
),
|
||||
)
|
||||
parser.set_defaults(func=install_apps)
|
||||
|
||||
def add_update_subparser(subparsers):
|
||||
def update_apps(args):
|
||||
|
||||
def add_update_subparser(subparsers: ArgumentParser) -> None:
|
||||
def update_apps(args: Namespace) -> None:
|
||||
cm = ConfigManager(args.config_dir)
|
||||
cm.update_apps(apps=args.apps)
|
||||
|
||||
parser = subparsers.add_parser(
|
||||
'update',
|
||||
description='Run update scripts for registered applications.'
|
||||
"update", description="Run update scripts for registered applications."
|
||||
)
|
||||
parser.add_argument(
|
||||
'-a', '--apps',
|
||||
"-a",
|
||||
"--apps",
|
||||
required=False,
|
||||
default = '*',
|
||||
type = lambda s: s.split(',') if s != '*' else s,
|
||||
help = 'Application target for theme. App must be present in the registry. ' \
|
||||
+ 'Use "*" to apply to all registered apps'
|
||||
default="*",
|
||||
type=lambda s: s.split(",") if s != "*" else s,
|
||||
help=(
|
||||
"Application target for theme. App must be present in the "
|
||||
'registry. Use "*" to apply to all registered apps'
|
||||
),
|
||||
)
|
||||
parser.set_defaults(func=update_apps)
|
||||
|
||||
def add_config_subparser(subparsers):
|
||||
def configure_apps(args):
|
||||
|
||||
def add_config_subparser(subparsers: ArgumentParser) -> None:
|
||||
def configure_apps(args: Namespace) -> None:
|
||||
cm = ConfigManager(args.config_dir)
|
||||
cm.configure_apps(
|
||||
apps=args.apps,
|
||||
scheme=args.mode,
|
||||
style=args.style,
|
||||
**args.template_vars
|
||||
**args.template_vars,
|
||||
)
|
||||
|
||||
parser = subparsers.add_parser(
|
||||
'config',
|
||||
description='Set config files for registered applications.'
|
||||
"config", description="Set config files for registered applications."
|
||||
)
|
||||
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',
|
||||
"-s",
|
||||
"--style",
|
||||
required=False,
|
||||
default="any",
|
||||
help = 'Preferred lightness mode/scheme, either "light," "dark," "any," or "none."'
|
||||
help=(
|
||||
"Style indicator (often a color palette) capturing "
|
||||
"thematic details in a config file"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-a', '--apps',
|
||||
"-m",
|
||||
"--mode",
|
||||
required=False,
|
||||
default="any",
|
||||
help=(
|
||||
'Preferred lightness mode/scheme, either "light," "dark," '
|
||||
'"any," or "none."'
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--apps",
|
||||
required=False,
|
||||
default="*",
|
||||
type = lambda s: s.split(',') if s != '*' else s,
|
||||
help = 'Application target for theme. App must be present in the registry. ' \
|
||||
+ 'Use "*" to apply to all registered apps'
|
||||
type=lambda s: s.split(",") if s != "*" else s,
|
||||
help=(
|
||||
"Application target for theme. App must be present in the "
|
||||
'registry. Use "*" to apply to all registered apps'
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-T', '--template-vars',
|
||||
"-T",
|
||||
"--template-vars",
|
||||
required=False,
|
||||
nargs='+',
|
||||
nargs="+",
|
||||
default={},
|
||||
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"
|
||||
),
|
||||
)
|
||||
parser.set_defaults(func=configure_apps)
|
||||
|
||||
def add_generate_subparser(subparsers):
|
||||
def generate_apps(args):
|
||||
|
||||
def add_generate_subparser(subparsers: ArgumentParser) -> None:
|
||||
def generate_apps(args: Namespace) -> None:
|
||||
cm = ConfigManager(args.config_dir)
|
||||
cm.generate_app_templates(
|
||||
gen_dir=args.output_dir,
|
||||
apps=args.apps,
|
||||
scheme=args.mode,
|
||||
style=args.style,
|
||||
**args.template_vars
|
||||
**args.template_vars,
|
||||
)
|
||||
|
||||
parser = subparsers.add_parser(
|
||||
'generate',
|
||||
description='Generate all template config files for specified apps'
|
||||
"generate",
|
||||
description="Generate all template config files for specified apps",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-o', '--output-dir',
|
||||
"-o",
|
||||
"--output-dir",
|
||||
required=True,
|
||||
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',
|
||||
"-s",
|
||||
"--style",
|
||||
required=False,
|
||||
default="any",
|
||||
help = 'Preferred lightness mode/scheme, either "light," "dark," "any," or "none."'
|
||||
help=(
|
||||
"Style indicator (often a color palette) capturing "
|
||||
"thematic details in a config file"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-a', '--apps',
|
||||
"-m",
|
||||
"--mode",
|
||||
required=False,
|
||||
default="any",
|
||||
help=(
|
||||
'Preferred lightness mode/scheme, either "light," "dark," '
|
||||
'"any," or "none."'
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--apps",
|
||||
required=False,
|
||||
default="*",
|
||||
type = lambda s: s.split(',') if s != '*' else s,
|
||||
help = 'Application target for theme. App must be present in the registry. ' \
|
||||
+ 'Use "*" to apply to all registered apps'
|
||||
type=lambda s: s.split(",") if s != "*" else s,
|
||||
help=(
|
||||
"Application target for theme. App must be present in the "
|
||||
'registry. Use "*" to apply to all registered apps'
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
'-T', '--template-vars',
|
||||
"-T",
|
||||
"--template-vars",
|
||||
required=False,
|
||||
nargs = '+',
|
||||
nargs="+",
|
||||
default={},
|
||||
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"
|
||||
),
|
||||
)
|
||||
parser.set_defaults(func=generate_apps)
|
||||
|
||||
|
||||
# central argparse entry point
|
||||
parser = argparse.ArgumentParser(
|
||||
'symconf',
|
||||
description='Manage application configuration with symlinks.'
|
||||
parser = ArgumentParser(
|
||||
"symconf", description="Manage application configuration with symlinks."
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--config-dir',
|
||||
"-c",
|
||||
"--config-dir",
|
||||
default=util.xdg_config_path(),
|
||||
type=util.absolute_path,
|
||||
help = 'Path to config directory'
|
||||
help="Path to config directory",
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v', '--version',
|
||||
action='version',
|
||||
"-v",
|
||||
"--version",
|
||||
action="version",
|
||||
version=__version__,
|
||||
help = 'Print symconf version'
|
||||
help="Print symconf version",
|
||||
)
|
||||
|
||||
# add subparsers
|
||||
subparsers = parser.add_subparsers(title='subcommand actions')
|
||||
subparsers = parser.add_subparsers(title="subcommand actions")
|
||||
add_config_subparser(subparsers)
|
||||
add_generate_subparser(subparsers)
|
||||
add_install_subparser(subparsers)
|
||||
add_update_subparser(subparsers)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
args = parser.parse_args()
|
||||
|
||||
if 'func' in args:
|
||||
if "func" in args:
|
||||
args.func(args)
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
Generic combinatorial name-matching subsystem
|
||||
|
||||
Config files are expected to have names matching the following spec:
|
||||
@ -7,13 +7,14 @@ Config files are expected to have names matching the following spec:
|
||||
|
||||
<style>-<scheme>.<config_pathname>
|
||||
|
||||
- ``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,
|
||||
however, it merely serves as an identifier, as it can be mapped onto any path.
|
||||
- ``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, however, it merely serves as an identifier,
|
||||
as it can be mapped onto any path.
|
||||
- ``scheme``: indicates the lightness mode ("light" or "dark")
|
||||
- ``style``: general identifier capturing the stylizations applied to the config file.
|
||||
This is typically of the form ``<variant>-<palette>``, i.e., including a reference to a
|
||||
particular color palette.
|
||||
- ``style``: general identifier capturing the stylizations applied to the
|
||||
config file. This is typically of the form ``<variant>-<palette>``, i.e.,
|
||||
including a reference to a particular color palette.
|
||||
|
||||
For example
|
||||
|
||||
@ -28,7 +29,7 @@ gets mapped to
|
||||
style -> "soft-gruvbox"
|
||||
scheme -> "dark"
|
||||
pathname -> "kitty.conf"
|
||||
'''
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
@ -36,24 +37,26 @@ from symconf import util
|
||||
|
||||
|
||||
class FilePart:
|
||||
def __init__(self, path: str | Path):
|
||||
def __init__(self, path: str | Path) -> None:
|
||||
self.path = util.absolute_path(path)
|
||||
self.pathname = self.path.name
|
||||
|
||||
parts = str(self.pathname).split('.')
|
||||
parts = str(self.pathname).split(".")
|
||||
if len(parts) < 2:
|
||||
raise ValueError(f'Filename "{pathname}" incorrectly formatted, ignoring')
|
||||
raise ValueError(
|
||||
f'Filename "{self.pathname}" incorrectly formatted, ignoring'
|
||||
)
|
||||
|
||||
self.theme = parts[0]
|
||||
self.conf = '.'.join(parts[1:])
|
||||
self.conf = ".".join(parts[1:])
|
||||
|
||||
theme_split = self.theme.split('-')
|
||||
theme_split = self.theme.split("-")
|
||||
self.scheme = theme_split[-1]
|
||||
self.style = '-'.join(theme_split[:-1])
|
||||
self.style = "-".join(theme_split[:-1])
|
||||
|
||||
self.index = -1
|
||||
|
||||
def set_index(self, idx: int):
|
||||
def set_index(self, idx: int) -> None:
|
||||
self.index = idx
|
||||
|
||||
|
||||
@ -62,7 +65,7 @@ class Matcher:
|
||||
self,
|
||||
paths: list[str | Path],
|
||||
) -> list[FilePart]:
|
||||
'''
|
||||
"""
|
||||
Split pathnames into parts for matching.
|
||||
|
||||
Pathnames should be of the format
|
||||
@ -71,74 +74,78 @@ class Matcher:
|
||||
|
||||
<style>-<scheme>.<config_pathname>
|
||||
|
||||
where ``style`` is typically itself of the form ``<variant>-<palette>``.
|
||||
'''
|
||||
where ``style`` is typically itself of the form
|
||||
``<variant>-<palette>``.
|
||||
"""
|
||||
|
||||
file_parts = []
|
||||
for path in paths:
|
||||
try:
|
||||
config_file = FilePart(path)
|
||||
file_parts.append(config_file)
|
||||
except ValueError as e:
|
||||
print(f'Filename "{pathname}" incorrectly formatted, ignoring')
|
||||
except ValueError:
|
||||
print(f'Filename "{path}" incorrectly formatted, ignoring')
|
||||
|
||||
return file_parts
|
||||
|
||||
def prefix_order(
|
||||
self,
|
||||
scheme,
|
||||
style,
|
||||
strict=False,
|
||||
scheme: str,
|
||||
style: str,
|
||||
strict: bool = False,
|
||||
) -> list[tuple[str, str]]:
|
||||
'''
|
||||
Determine the order of concrete config pathname parts to match, given the
|
||||
``scheme`` and ``style`` inputs.
|
||||
"""
|
||||
Determine the order of concrete config pathname parts to match, given
|
||||
the ``scheme`` and ``style`` inputs.
|
||||
|
||||
There is a unique preferred match order when ``style``, ``scheme``, both, or none
|
||||
are ``any``. In general, when ``any`` is provided for a given factor, it is
|
||||
best matched by a config file that expresses indifference under that factor.
|
||||
'''
|
||||
There is a unique preferred match order when ``style``, ``scheme``,
|
||||
both, or none are ``any``. In general, when ``any`` is provided for a
|
||||
given factor, it is best matched by a config file that expresses
|
||||
indifference under that factor.
|
||||
"""
|
||||
|
||||
# explicit cases are the most easily managed here, even if a little redundant
|
||||
# explicit cases are the most easily managed here, even if a little
|
||||
# redundant
|
||||
if strict:
|
||||
theme_order = [
|
||||
(style, scheme),
|
||||
]
|
||||
else:
|
||||
# inverse order of match relaxation; intention being to overwrite with
|
||||
# results from increasingly relevant groups given the conditions
|
||||
if style == 'any' and scheme == 'any':
|
||||
# inverse order of match relaxation; intention being to overwrite
|
||||
# with results from increasingly relevant groups given the
|
||||
# conditions
|
||||
if style == "any" and scheme == "any":
|
||||
# prefer both be "none", with preference for specific scheme
|
||||
theme_order = [
|
||||
(style, scheme),
|
||||
(style , 'none'),
|
||||
('none' , scheme),
|
||||
('none' , 'none'),
|
||||
(style, "none"),
|
||||
("none", scheme),
|
||||
("none", "none"),
|
||||
]
|
||||
elif style == 'any':
|
||||
# prefer style to be "none", then specific, then relax specific scheme
|
||||
# to "none"
|
||||
elif style == "any":
|
||||
# prefer style to be "none", then specific, then relax specific
|
||||
# scheme to "none"
|
||||
theme_order = [
|
||||
(style , 'none'),
|
||||
('none' , 'none'),
|
||||
(style, "none"),
|
||||
("none", "none"),
|
||||
(style, scheme),
|
||||
('none' , scheme),
|
||||
("none", scheme),
|
||||
]
|
||||
elif scheme == 'any':
|
||||
# prefer scheme to be "none", then specific, then relax specific style
|
||||
# to "none"
|
||||
elif scheme == "any":
|
||||
# prefer scheme to be "none", then specific, then relax
|
||||
# specific style to "none"
|
||||
theme_order = [
|
||||
('none' , scheme),
|
||||
('none' , 'none'),
|
||||
("none", scheme),
|
||||
("none", "none"),
|
||||
(style, scheme),
|
||||
(style , 'none'),
|
||||
(style, "none"),
|
||||
]
|
||||
else:
|
||||
# neither component is any; prefer most specific
|
||||
theme_order = [
|
||||
('none' , 'none'),
|
||||
('none' , scheme),
|
||||
(style , 'none'),
|
||||
("none", "none"),
|
||||
("none", scheme),
|
||||
(style, "none"),
|
||||
(style, scheme),
|
||||
]
|
||||
|
||||
@ -149,45 +156,46 @@ class Matcher:
|
||||
paths: list[str | Path],
|
||||
prefix_order: list[tuple[str, str]],
|
||||
) -> list[FilePart]:
|
||||
'''
|
||||
Find and return FilePart matches according to the provided prefix order.
|
||||
"""
|
||||
Find and return FilePart matches according to the provided prefix
|
||||
order.
|
||||
|
||||
The prefix order specifies all valid style-scheme combos that can be considered as
|
||||
"consistent" with some user input (and is computed external to this method). For
|
||||
example, it could be
|
||||
The prefix order specifies all valid style-scheme combos that can be
|
||||
considered as "consistent" with some user input (and is computed
|
||||
external to this method). For example, it could be
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
[
|
||||
('none', 'none')
|
||||
('none', 'dark')
|
||||
]
|
||||
[("none", "none")("none", "dark")]
|
||||
|
||||
indicating that either ``none-none.<config>`` or ``none-dark.<config>`` would be
|
||||
considered matching pathnames, with the latter being preferred.
|
||||
indicating that either ``none-none.<config>`` or ``none-dark.<config>``
|
||||
would be considered matching pathnames, with the latter being
|
||||
preferred.
|
||||
|
||||
This method exists because we need a way to allow any of the combos in the prefix
|
||||
order to match the candidate files. We don't know a priori how good of a match
|
||||
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).
|
||||
This method exists because we need a way to allow any of the combos in
|
||||
the prefix order to match the candidate files. We don't know a priori
|
||||
how good of a match 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).
|
||||
|
||||
.. admonition:: Checking for matches
|
||||
|
||||
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
|
||||
inner loop that needs to inspect each FilePart for each element of the prefix
|
||||
order. But indexing the file parts and checking against prefixes isn't so
|
||||
straightforward, as we'd still need to check matches by factor. For instance,
|
||||
if we index by style-scheme, either are allowed to be "any," so we'd need to
|
||||
check for the 4 valid combos and join the matching lists. If we index by both
|
||||
factors individually, we may have several files associated with a given key,
|
||||
and then need to coordinate the checks across both to ensure they belong to
|
||||
the same file.
|
||||
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 inner loop that needs to inspect each
|
||||
FilePart for each element of the prefix order. But indexing the
|
||||
file parts and checking against prefixes isn't so straightforward,
|
||||
as we'd still need to check matches by factor. For instance, if we
|
||||
index by style-scheme, either are allowed to be "any," so we'd need
|
||||
to check for the 4 valid combos and join the matching lists. If we
|
||||
index by both factors individually, we may have several files
|
||||
associated with a given key, and then need to coordinate the checks
|
||||
across both to ensure they belong to the same file.
|
||||
|
||||
In any case, you should be able to do this in a way that's a bit more
|
||||
efficient, but the loop and the simple conditionals is just much simpler to
|
||||
follow. We're also talking about at most 10s of files, so it really doesn't
|
||||
matter.
|
||||
In any case, you should be able to do this in a way that's a bit
|
||||
more efficient, but the loop and the simple conditionals is just
|
||||
much simpler to follow. We're also talking about at most 10s of
|
||||
files, so it really doesn't matter.
|
||||
|
||||
Parameters:
|
||||
pathnames:
|
||||
@ -195,15 +203,17 @@ class Matcher:
|
||||
style:
|
||||
prefix_order:
|
||||
strict:
|
||||
'''
|
||||
"""
|
||||
|
||||
file_parts = self.get_file_parts(paths)
|
||||
|
||||
ordered_matches = []
|
||||
for i, (style_prefix, scheme_prefix) in enumerate(prefix_order):
|
||||
for fp in file_parts:
|
||||
style_match = style_prefix == fp.style or style_prefix == 'any'
|
||||
scheme_match = scheme_prefix == fp.scheme or scheme_prefix == 'any'
|
||||
style_match = style_prefix == fp.style or style_prefix == "any"
|
||||
scheme_match = (
|
||||
scheme_prefix == fp.scheme or scheme_prefix == "any"
|
||||
)
|
||||
|
||||
if style_match and scheme_match:
|
||||
fp.set_index(i + 1)
|
||||
@ -211,35 +221,35 @@ class Matcher:
|
||||
|
||||
return ordered_matches
|
||||
|
||||
def relaxed_match(
|
||||
self,
|
||||
match_list: list[FilePart]
|
||||
) -> list[FilePart]:
|
||||
'''
|
||||
def relaxed_match(self, match_list: list[FilePart]) -> list[FilePart]:
|
||||
"""
|
||||
Isolate the best match in a match list and find its relaxed variants.
|
||||
|
||||
This method allows us to use the ``match_paths()`` method for matching templates
|
||||
rather than direct user config files. In the latter case, we want to symlink the
|
||||
single best config file match for each stem, across all stems with matching
|
||||
prefixes (e.g., ``none-dark.config.a`` and ``solarized-dark.config.b`` have two
|
||||
separate stems with prefixes that could match ``scheme=dark, style=any`` query).
|
||||
We can find these files by just indexing the ``match_path`` outputs (i.e., all
|
||||
matches) by config pathname and taking the one that appears latest (under the
|
||||
This method allows us to use the ``match_paths()`` method for matching
|
||||
templates rather than direct user config files. In the latter case, we
|
||||
want to symlink the single best config file match for each stem, across
|
||||
all stems with matching prefixes (e.g., ``none-dark.config.a`` and
|
||||
``solarized-dark.config.b`` have two separate stems with prefixes that
|
||||
could match ``scheme=dark, style=any`` query). We can find these files
|
||||
by just indexing the ``match_path`` outputs (i.e., all matches) by
|
||||
config pathname and taking the one that appears latest (under the
|
||||
prefix order) for each unique value.
|
||||
|
||||
In the template matching case, we want only a single best file match, period
|
||||
(there's really no notion of "config stems," it's just the prefixes). Once that
|
||||
match has been found, we can then "relax" either the scheme or style (or both) to
|
||||
``none``, and if the corresponding files exist, we use those as parts of the
|
||||
template keys. For example, if we match ``solarized-dark.toml``, we would also
|
||||
consider the values in ``none-dark.toml`` if available. The TOML values that are
|
||||
defined in the most specific (i.e., better under the prefix order) match are
|
||||
loaded "on top of" those less specific matches, overwriting keys when there's a
|
||||
conflict. ``none-dark.toml``, for instance, might define a general dark scheme
|
||||
background color, but a more specific definition in ``solarized-dark.toml`` would
|
||||
take precedent. These TOML files would be stacked before using the resulting
|
||||
dictionary to populate config templates.
|
||||
'''
|
||||
In the template matching case, we want only a single best file match,
|
||||
period (there's really no notion of "config stems," it's just the
|
||||
prefixes). Once that match has been found, we can then "relax" either
|
||||
the scheme or style (or both) to ``none``, and if the corresponding
|
||||
files exist, we use those as parts of the template keys. For example,
|
||||
if we match ``solarized-dark.toml``, we would also consider the values
|
||||
in ``none-dark.toml`` if available. The TOML values that are defined in
|
||||
the most specific (i.e., better under the prefix order) match are
|
||||
loaded "on top of" those less specific matches, overwriting keys when
|
||||
there's a conflict. ``none-dark.toml``, for instance, might define a
|
||||
general dark scheme background color, but a more specific definition in
|
||||
``solarized-dark.toml`` would take precedent. These TOML files would be
|
||||
stacked before using the resulting dictionary to populate config
|
||||
templates.
|
||||
"""
|
||||
|
||||
if not match_list:
|
||||
return []
|
||||
@ -248,11 +258,10 @@ class Matcher:
|
||||
match = match_list[-1]
|
||||
|
||||
for fp in match_list:
|
||||
style_match = fp.style == match.style or fp.style == 'none'
|
||||
scheme_match = fp.scheme == match.scheme or fp.scheme == 'none'
|
||||
style_match = fp.style == match.style or fp.style == "none"
|
||||
scheme_match = fp.scheme == match.scheme or fp.scheme == "none"
|
||||
|
||||
if style_match and scheme_match:
|
||||
relaxed_map[fp.pathname] = fp
|
||||
|
||||
return list(relaxed_map.values())
|
||||
|
||||
|
@ -1,39 +1,44 @@
|
||||
'''
|
||||
"""
|
||||
Simplified management for nested dictionaries
|
||||
'''
|
||||
"""
|
||||
|
||||
import copy
|
||||
import pprint
|
||||
import tomllib
|
||||
import hashlib
|
||||
import logging
|
||||
import tomllib
|
||||
from typing import Any
|
||||
from pathlib import Path
|
||||
|
||||
from symconf.util import deep_update
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DictReader:
|
||||
def __init__(self, toml_path=None):
|
||||
def __init__(self, toml_path: str | None = None) -> None:
|
||||
self._config = {}
|
||||
self.toml_path = toml_path
|
||||
|
||||
if toml_path is not None:
|
||||
self._config = self._load_toml(toml_path)
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return pprint.pformat(self._config, indent=4)
|
||||
|
||||
@staticmethod
|
||||
def _load_toml(toml_path) -> dict[str, Any]:
|
||||
def _load_toml(toml_path: str) -> dict[str, Any]:
|
||||
return tomllib.loads(Path(toml_path).read_text())
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, config_dict):
|
||||
def from_dict(cls, config_dict: dict) -> "DictReader":
|
||||
new_instance = cls()
|
||||
new_instance._config = copy.deepcopy(config_dict)
|
||||
return new_instance
|
||||
|
||||
def update(self, config, in_place=False):
|
||||
def update(
|
||||
self, config: "DictReader", in_place: bool = False
|
||||
) -> "DictReader":
|
||||
new_config = deep_update(self._config, config._config)
|
||||
|
||||
if in_place:
|
||||
@ -42,13 +47,14 @@ class DictReader:
|
||||
|
||||
return self.from_dict(new_config)
|
||||
|
||||
def copy(self):
|
||||
def copy(self) -> "DictReader":
|
||||
return self.from_dict(copy.deepcopy(self._config))
|
||||
|
||||
def get_subconfig(self, key): pass
|
||||
def get_subconfig(self, key: str) -> "DictReader":
|
||||
pass
|
||||
|
||||
def get(self, key, default=None):
|
||||
keys = key.split('.')
|
||||
def get(self, key: str, default: str | None = None) -> str:
|
||||
keys = key.split(".")
|
||||
|
||||
subconfig = self._config
|
||||
for subkey in keys[:-1]:
|
||||
@ -59,8 +65,8 @@ class DictReader:
|
||||
|
||||
return subconfig.get(keys[-1], default)
|
||||
|
||||
def set(self, key, value):
|
||||
keys = key.split('.')
|
||||
def set(self, key: str, value: str) -> bool:
|
||||
keys = key.split(".")
|
||||
|
||||
subconfig = self._config
|
||||
for subkey in keys[:-1]:
|
||||
@ -69,7 +75,8 @@ class DictReader:
|
||||
|
||||
if type(subconfig) is not dict:
|
||||
logger.debug(
|
||||
'Attempting to set nested key with an existing non-dict parent'
|
||||
"Attempting to set nested key with an "
|
||||
"existing non-dict parent"
|
||||
)
|
||||
return False
|
||||
|
||||
@ -80,9 +87,10 @@ class DictReader:
|
||||
subconfig = subdict
|
||||
|
||||
subconfig.update({keys[-1]: value})
|
||||
|
||||
return True
|
||||
|
||||
def generate_hash(self, exclude_keys=None):
|
||||
def generate_hash(self, exclude_keys: list[str] | None = None) -> str:
|
||||
inst_copy = self.copy()
|
||||
|
||||
if exclude_keys is not None:
|
||||
@ -93,6 +101,5 @@ class DictReader:
|
||||
|
||||
# create hash from config options
|
||||
config_str = str(sorted(items))
|
||||
|
||||
return hashlib.md5(config_str.encode()).hexdigest()
|
||||
|
||||
|
||||
|
@ -1,54 +1,53 @@
|
||||
'''
|
||||
"""
|
||||
Handle job/script execution
|
||||
'''
|
||||
"""
|
||||
|
||||
import stat
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from colorama import Fore, Back, Style
|
||||
from colorama import Fore, Style
|
||||
|
||||
from symconf.util import printc, color_text
|
||||
from symconf.util import color_text
|
||||
|
||||
|
||||
class Runner:
|
||||
def run_script(
|
||||
self,
|
||||
script: str | Path,
|
||||
):
|
||||
) -> str | None:
|
||||
script_path = Path(script)
|
||||
|
||||
if script_path.stat().st_mode & stat.S_IXUSR == 0:
|
||||
print(
|
||||
color_text("│", Fore.BLUE),
|
||||
color_text(
|
||||
f' > script "{script_path.name}" missing execute permissions, skipping',
|
||||
Fore.RED + Style.DIM
|
||||
)
|
||||
f' > script "{script_path.name}" missing '
|
||||
"execute permissions, skipping",
|
||||
Fore.RED + Style.DIM,
|
||||
),
|
||||
)
|
||||
return
|
||||
|
||||
print(
|
||||
color_text("│", Fore.BLUE),
|
||||
color_text(
|
||||
f' > running script "{script_path.name}"',
|
||||
Fore.BLUE
|
||||
)
|
||||
color_text(f' > running script "{script_path.name}"', Fore.BLUE),
|
||||
)
|
||||
|
||||
output = subprocess.check_output(str(script_path), shell=True)
|
||||
|
||||
if output:
|
||||
fmt_output = output.decode().strip().replace(
|
||||
'\n',
|
||||
f'\n{Fore.BLUE}{Style.NORMAL}│{Style.DIM} '
|
||||
fmt_output = (
|
||||
output.decode()
|
||||
.strip()
|
||||
.replace("\n", f"\n{Fore.BLUE}{Style.NORMAL}│{Style.DIM} ")
|
||||
)
|
||||
print(
|
||||
color_text("│", Fore.BLUE),
|
||||
color_text(
|
||||
f' captured script output "{fmt_output}"',
|
||||
Fore.BLUE + Style.DIM
|
||||
)
|
||||
Fore.BLUE + Style.DIM,
|
||||
),
|
||||
)
|
||||
|
||||
return output
|
||||
@ -56,7 +55,7 @@ class Runner:
|
||||
def run_many(
|
||||
self,
|
||||
script_list: list[str | Path],
|
||||
):
|
||||
) -> list[str | None]:
|
||||
outputs = []
|
||||
for script in script_list:
|
||||
output = self.run_script(script)
|
||||
|
@ -1,6 +1,6 @@
|
||||
'''
|
||||
"""
|
||||
Support for basic config templates
|
||||
'''
|
||||
"""
|
||||
|
||||
import re
|
||||
import tomllib
|
||||
@ -14,9 +14,9 @@ class Template:
|
||||
def __init__(
|
||||
self,
|
||||
template_str: str,
|
||||
key_pattern : str = r'f{{(\S+?)}}',
|
||||
exe_pattern : str = r'x{{((?:(?!x{{).)*)}}',
|
||||
):
|
||||
key_pattern: str = r"f{{(\S+?)}}",
|
||||
exe_pattern: str = r"x{{((?:(?!x{{).)*)}}",
|
||||
) -> None:
|
||||
self.template_str = template_str
|
||||
self.key_pattern = key_pattern
|
||||
self.exe_pattern = exe_pattern
|
||||
@ -30,20 +30,18 @@ class Template:
|
||||
exe_filled = re.sub(
|
||||
self.exe_pattern,
|
||||
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
|
||||
self.key_pattern, lambda m: self._key_fill(m, dr), exe_filled
|
||||
)
|
||||
|
||||
return key_filled
|
||||
|
||||
def _key_fill(
|
||||
self,
|
||||
match,
|
||||
match: re.Match,
|
||||
dict_reader: DictReader,
|
||||
) -> str:
|
||||
key = match.group(1)
|
||||
@ -52,13 +50,13 @@ class Template:
|
||||
|
||||
def _exe_fill(
|
||||
self,
|
||||
match,
|
||||
match: re.Match,
|
||||
dict_reader: DictReader,
|
||||
) -> str:
|
||||
key_fill = re.sub(
|
||||
self.key_pattern,
|
||||
lambda m: f'"{self._key_fill(m, dict_reader)}"',
|
||||
match.group(1)
|
||||
match.group(1),
|
||||
)
|
||||
|
||||
return str(eval(key_fill))
|
||||
@ -68,11 +66,11 @@ class FileTemplate(Template):
|
||||
def __init__(
|
||||
self,
|
||||
path: Path,
|
||||
key_pattern: str = r'f{{(\S+?)}}',
|
||||
exe_pattern : str = r'x{{((?:(?!x{{).)*)}}',
|
||||
):
|
||||
key_pattern: str = r"f{{(\S+?)}}",
|
||||
exe_pattern: str = r"x{{((?:(?!x{{).)*)}}",
|
||||
) -> None:
|
||||
super().__init__(
|
||||
path.open('r').read(),
|
||||
path.open("r").read(),
|
||||
key_pattern=key_pattern,
|
||||
exe_pattern=exe_pattern,
|
||||
)
|
||||
@ -82,9 +80,9 @@ class TOMLTemplate(FileTemplate):
|
||||
def __init__(
|
||||
self,
|
||||
toml_path: Path,
|
||||
key_pattern: str = r'f{{(\S+?)}}',
|
||||
exe_pattern : str = r'x{{((?:(?!x{{).)*)}}',
|
||||
):
|
||||
key_pattern: str = r"f{{(\S+?)}}",
|
||||
exe_pattern: str = r"x{{((?:(?!x{{).)*)}}",
|
||||
) -> None:
|
||||
super().__init__(
|
||||
toml_path,
|
||||
key_pattern=key_pattern,
|
||||
@ -101,12 +99,10 @@ class TOMLTemplate(FileTemplate):
|
||||
return toml_dict
|
||||
|
||||
@staticmethod
|
||||
def stack_toml(
|
||||
path_list: list[Path]
|
||||
) -> dict:
|
||||
def stack_toml(path_list: list[Path]) -> dict:
|
||||
stacked_dict = {}
|
||||
for toml_path in path_list:
|
||||
updated_map = tomllib.load(toml_path.open('rb'))
|
||||
updated_map = tomllib.load(toml_path.open("rb"))
|
||||
stacked_dict = util.deep_update(stacked_dict, updated_map)
|
||||
|
||||
return stacked_dict
|
||||
|
@ -1,34 +1,35 @@
|
||||
import re
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from argparse import Action, Namespace, ArgumentParser
|
||||
|
||||
from xdg import BaseDirectory
|
||||
from colorama import Fore, Back, Style
|
||||
from colorama.ansi import AnsiFore, AnsiBack, AnsiStyle
|
||||
from colorama import Back, Fore, Style
|
||||
from colorama.ansi import AnsiCodes
|
||||
|
||||
|
||||
def color_text(text, *colorama_args):
|
||||
'''
|
||||
def color_text(text: str, *colorama_args: AnsiCodes) -> str:
|
||||
"""
|
||||
Colorama text helper function
|
||||
|
||||
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
|
||||
changing styles in one group, and an inner call is changing styles in another, but
|
||||
*not* when affected groups overlap.
|
||||
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 changing styles in one group, and an inner call is
|
||||
changing styles in another, but *not* when affected groups overlap.
|
||||
|
||||
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
|
||||
background or style with affecting the foreground. The primary use case here is
|
||||
styling a group of text a single color, but applying ``BRIGHT`` or ``DIM`` styles only
|
||||
to some text elements within. If we didn't reset by group, the outer coloration
|
||||
request will be "canceled out" as soon as the first inner call is made (since the
|
||||
unconditional behavior just employs ``Style.RESET_ALL``).
|
||||
'''
|
||||
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 background or style with affecting the foreground.
|
||||
The primary use case here is styling a group of text a single color, but
|
||||
applying ``BRIGHT`` or ``DIM`` styles only to some text elements within. If
|
||||
we didn't reset by group, the outer coloration request will be "canceled
|
||||
out" as soon as the first inner call is made (since the unconditional
|
||||
behavior just employs ``Style.RESET_ALL``).
|
||||
"""
|
||||
|
||||
# reverse map colorama Ansi codes
|
||||
resets = []
|
||||
for carg in colorama_args:
|
||||
match = re.match(r'.*\[(\d+)m', carg)
|
||||
match = re.match(r".*\[(\d+)m", carg)
|
||||
if match:
|
||||
intv = int(match.group(1))
|
||||
if (intv >= 30 and intv <= 39) or (intv >= 90 and intv <= 97):
|
||||
@ -40,44 +41,62 @@ def color_text(text, *colorama_args):
|
||||
|
||||
return f"{''.join(colorama_args)}{text}{''.join(resets)}"
|
||||
|
||||
def printc(text, *colorama_args):
|
||||
|
||||
def printc(text: str, *colorama_args: AnsiCodes) -> None:
|
||||
print(color_text(text, *colorama_args))
|
||||
|
||||
|
||||
def absolute_path(path: str | Path) -> Path:
|
||||
return Path(path).expanduser().absolute()
|
||||
|
||||
def xdg_config_path():
|
||||
return Path(BaseDirectory.save_config_path('symconf'))
|
||||
|
||||
def xdg_config_path() -> Path:
|
||||
return Path(BaseDirectory.save_config_path("symconf"))
|
||||
|
||||
|
||||
def to_tilde_path(path: Path) -> Path:
|
||||
'''
|
||||
"""
|
||||
Abbreviate an absolute path by replacing HOME with "~", if applicable.
|
||||
'''
|
||||
"""
|
||||
|
||||
try:
|
||||
return Path(f"~/{path.relative_to(Path.home())}")
|
||||
except ValueError:
|
||||
return path
|
||||
|
||||
|
||||
def deep_update(mapping: dict, *updating_mappings: dict) -> dict:
|
||||
'''Code adapted from pydantic'''
|
||||
"""Code adapted from pydantic"""
|
||||
|
||||
updated_mapping = mapping.copy()
|
||||
for updating_mapping in updating_mappings:
|
||||
for k, v in updating_mapping.items():
|
||||
if k in updated_mapping and isinstance(updated_mapping[k], dict) and isinstance(v, dict):
|
||||
if (
|
||||
k in updated_mapping
|
||||
and isinstance(updated_mapping[k], dict)
|
||||
and isinstance(v, dict)
|
||||
):
|
||||
updated_mapping[k] = deep_update(updated_mapping[k], v)
|
||||
else:
|
||||
updated_mapping[k] = v
|
||||
return updated_mapping
|
||||
|
||||
|
||||
class KVPair(argparse.Action):
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
class KVPair(Action):
|
||||
def __call__(
|
||||
self,
|
||||
parser: ArgumentParser,
|
||||
namespace: Namespace,
|
||||
values: list[str],
|
||||
option_string: str | None = None,
|
||||
) -> None:
|
||||
kv_dict = getattr(namespace, self.dest, {})
|
||||
|
||||
if kv_dict is None:
|
||||
kv_dict = {}
|
||||
|
||||
for value in values:
|
||||
key, val = value.split('=', 1)
|
||||
key, val = value.split("=", 1)
|
||||
kv_dict[key] = val
|
||||
|
||||
setattr(namespace, self.dest, kv_dict)
|
||||
|
@ -1,10 +0,0 @@
|
||||
def test_imports():
|
||||
from symconf.runner import Runner
|
||||
from symconf.reader import DictReader
|
||||
from symconf.config import ConfigManager
|
||||
from symconf.matching import Matcher, FilePart
|
||||
from symconf.template import Template, FileTemplate, TOMLTemplate
|
||||
|
||||
from symconf import config
|
||||
from symconf import reader
|
||||
from symconf import util
|
@ -2,64 +2,64 @@ from pathlib import Path
|
||||
|
||||
from symconf import ConfigManager
|
||||
|
||||
|
||||
config_dir = Path(
|
||||
__file__, '..', 'test-config-dir/'
|
||||
).resolve()
|
||||
config_dir = Path(__file__, "..", "test-config-dir/").resolve()
|
||||
cm = ConfigManager(config_dir)
|
||||
|
||||
|
||||
def test_matching_configs_exact():
|
||||
'''
|
||||
Test matching exact style and scheme. Given strict mode not set (allowing relaxation
|
||||
to "none"), the order of matching should be
|
||||
def test_matching_configs_exact() -> None:
|
||||
"""
|
||||
Test matching exact style and scheme. Given strict mode not set (allowing
|
||||
relaxation to "none"), the order of matching should be
|
||||
|
||||
1. (none, none) :: none-none.aaa
|
||||
2. (none, scheme) :: none-light.aaa
|
||||
3. (style, none) :: test-none.aaa
|
||||
4. (style, scheme) :: test-light.ccc
|
||||
|
||||
Yielding "test-light.aaa", "test-light.ccc" (unique only on config pathname).
|
||||
'''
|
||||
Yielding "test-light.aaa", "test-light.ccc" (unique only on config
|
||||
pathname).
|
||||
"""
|
||||
any_light = cm.get_matching_configs(
|
||||
'test',
|
||||
style='test',
|
||||
scheme='light',
|
||||
"test",
|
||||
style="test",
|
||||
scheme="light",
|
||||
)
|
||||
print(any_light)
|
||||
|
||||
assert len(any_light) == 2
|
||||
assert any_light['aaa'].pathname == 'test-none.aaa'
|
||||
assert any_light['ccc'].pathname == 'test-light.ccc'
|
||||
assert any_light["aaa"].pathname == "test-none.aaa"
|
||||
assert any_light["ccc"].pathname == "test-light.ccc"
|
||||
|
||||
def test_matching_configs_any_style():
|
||||
'''
|
||||
Test matching "any" style and exact scheme. Given strict mode not set (allowing
|
||||
relaxation to "none"), the order of matching should be
|
||||
|
||||
def test_matching_configs_any_style() -> None:
|
||||
"""
|
||||
Test matching "any" style and exact scheme. Given strict mode not set
|
||||
(allowing relaxation to "none"), the order of matching should be
|
||||
|
||||
1. (style, none) :: none-none.aaa, test-none.aaa
|
||||
2. (none, none) :: none-none.aaa
|
||||
3. (style, scheme) :: test-dark.bbb
|
||||
4. (none, scheme) :: (nothing)
|
||||
|
||||
Yielding "none-none.aaa" (should always overwrite "test-none.aaa" due to "any"'s
|
||||
preference for non-specific matches, i.e., "none"s), "test-none.ddd", "test-dark.bbb"
|
||||
(unique only on config pathname).
|
||||
'''
|
||||
Yielding "none-none.aaa" (should always overwrite "test-none.aaa" due to
|
||||
"any"'s preference for non-specific matches, i.e., "none"s),
|
||||
"test-none.ddd", "test-dark.bbb" (unique only on config pathname).
|
||||
"""
|
||||
any_dark = cm.get_matching_configs(
|
||||
'test',
|
||||
style='any',
|
||||
scheme='dark',
|
||||
"test",
|
||||
style="any",
|
||||
scheme="dark",
|
||||
)
|
||||
|
||||
assert len(any_dark) == 2
|
||||
assert any_dark['aaa'].pathname == 'none-none.aaa'
|
||||
assert any_dark['bbb'].pathname == 'test-dark.bbb'
|
||||
assert any_dark["aaa"].pathname == "none-none.aaa"
|
||||
assert any_dark["bbb"].pathname == "test-dark.bbb"
|
||||
|
||||
def test_matching_configs_any_scheme():
|
||||
'''
|
||||
Test matching exact style and "any" scheme. Given strict mode not set (allowing
|
||||
relaxation to "none"), the order of matching should be
|
||||
|
||||
def test_matching_configs_any_scheme() -> None:
|
||||
"""
|
||||
Test matching exact style and "any" scheme. Given strict mode not set
|
||||
(allowing relaxation to "none"), the order of matching should be
|
||||
|
||||
1. (none, scheme) :: none-light.aaa & none-none.aaa
|
||||
2. (none, none) :: none-none.aaa
|
||||
@ -67,54 +67,64 @@ def test_matching_configs_any_scheme():
|
||||
4. (style, none) :: test-none.aaa
|
||||
|
||||
Yielding "test-none.aaa", "test-light.ccc", "test-dark.bbb"
|
||||
'''
|
||||
"""
|
||||
test_any = cm.get_matching_configs(
|
||||
'test',
|
||||
style='test',
|
||||
scheme='any',
|
||||
"test",
|
||||
style="test",
|
||||
scheme="any",
|
||||
)
|
||||
|
||||
assert len(test_any) == 3
|
||||
assert test_any['aaa'].pathname == 'test-none.aaa'
|
||||
assert test_any['bbb'].pathname == 'test-dark.bbb'
|
||||
assert test_any['ccc'].pathname == 'test-light.ccc'
|
||||
assert test_any["aaa"].pathname == "test-none.aaa"
|
||||
assert test_any["bbb"].pathname == "test-dark.bbb"
|
||||
assert test_any["ccc"].pathname == "test-light.ccc"
|
||||
|
||||
def test_matching_scripts():
|
||||
'''
|
||||
Test matching exact style and scheme. Given strict mode not set (allowing relaxation
|
||||
to "none"), the order of matching should be
|
||||
|
||||
def test_matching_scripts() -> None:
|
||||
"""
|
||||
Test matching exact style and scheme. Given strict mode not set (allowing
|
||||
relaxation to "none"), the order of matching should be
|
||||
|
||||
1. (none, none) :: none-none.sh
|
||||
2. (none, scheme) :: none-light.sh
|
||||
3. (style, none) :: test-none.sh
|
||||
4. (style, scheme) :: (nothing)
|
||||
|
||||
Yielding (ordered by dec specificity) "test-none.sh" as primary match, then relaxation
|
||||
match "none-none.sh".
|
||||
'''
|
||||
Yielding (ordered by dec specificity) "test-none.sh" as primary match, then
|
||||
relaxation match "none-none.sh".
|
||||
"""
|
||||
test_any = cm.get_matching_scripts(
|
||||
'test',
|
||||
style='test',
|
||||
scheme='any',
|
||||
"test",
|
||||
style="test",
|
||||
scheme="any",
|
||||
)
|
||||
|
||||
assert len(test_any) == 2
|
||||
assert list(map(lambda p:p.pathname, test_any)) == ['test-none.sh', 'none-none.sh']
|
||||
assert [p.pathname for p in test_any] == [
|
||||
"test-none.sh",
|
||||
"none-none.sh",
|
||||
]
|
||||
|
||||
any_light = cm.get_matching_scripts(
|
||||
'test',
|
||||
style='any',
|
||||
scheme='light',
|
||||
"test",
|
||||
style="any",
|
||||
scheme="light",
|
||||
)
|
||||
|
||||
assert len(any_light) == 2
|
||||
assert list(map(lambda p:p.pathname, any_light)) == ['none-light.sh', 'none-none.sh']
|
||||
assert [p.pathname for p in any_light] == [
|
||||
"none-light.sh",
|
||||
"none-none.sh",
|
||||
]
|
||||
|
||||
any_dark = cm.get_matching_scripts(
|
||||
'test',
|
||||
style='any',
|
||||
scheme='dark',
|
||||
"test",
|
||||
style="any",
|
||||
scheme="dark",
|
||||
)
|
||||
|
||||
assert len(any_dark) == 2
|
||||
assert list(map(lambda p:p.pathname, any_dark)) == ['test-none.sh', 'none-none.sh']
|
||||
assert [p.pathname for p in any_dark] == [
|
||||
"test-none.sh",
|
||||
"none-none.sh",
|
||||
]
|
||||
|
@ -2,30 +2,47 @@ from pathlib import Path
|
||||
|
||||
from symconf import Template, TOMLTemplate
|
||||
|
||||
def test_template_fill():
|
||||
|
||||
def test_template_fill() -> None:
|
||||
# test simple replacment
|
||||
assert Template('f{{a}} - f{{b}}').fill({
|
||||
'a': 1,
|
||||
'b': 2,
|
||||
}) == '1 - 2'
|
||||
assert (
|
||||
Template("f{{a}} - f{{b}}").fill(
|
||||
{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
)
|
||||
== "1 - 2"
|
||||
)
|
||||
|
||||
# test nested brackets (using default pattern)
|
||||
assert Template('{{ f{{a}} - f{{b}} }}').fill({
|
||||
'a': 1,
|
||||
'b': 2,
|
||||
}) == '{{ 1 - 2 }}'
|
||||
assert (
|
||||
Template("{{ f{{a}} - f{{b}} }}").fill(
|
||||
{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
)
|
||||
== "{{ 1 - 2 }}"
|
||||
)
|
||||
|
||||
# test tight nested brackets (requires greedy quantifier)
|
||||
assert Template('{{f{{a}} - f{{b}}}}').fill({
|
||||
'a': 1,
|
||||
'b': 2,
|
||||
}) == '{{1 - 2}}'
|
||||
assert (
|
||||
Template("{{f{{a}} - f{{b}}}}").fill(
|
||||
{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
)
|
||||
== "{{1 - 2}}"
|
||||
)
|
||||
|
||||
def test_toml_template_fill():
|
||||
|
||||
def test_toml_template_fill() -> None:
|
||||
test_group_dir = Path(
|
||||
__file__, '..', 'test-config-dir/groups/test/'
|
||||
__file__, "..", "test-config-dir/groups/test/"
|
||||
).resolve()
|
||||
|
||||
stacked_dict = TOMLTemplate.stack_toml(test_group_dir.iterdir())
|
||||
|
||||
assert stacked_dict == {'base':'aaa','concrete':'zzz'}
|
||||
assert stacked_dict == {"base": "aaa", "concrete": "zzz"}
|
||||
|
343
uv.lock
generated
343
uv.lock
generated
@ -45,20 +45,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a", size = 105113, upload-time = "2025-08-24T14:06:14.884Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "build"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "os_name == 'nt'" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pyproject-hooks" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/1c/23e33405a7c9eac261dff640926b8b5adaed6a6eb3e1767d441ed611d0c0/build-1.3.0.tar.gz", hash = "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397", size = 48544, upload-time = "2025-08-01T21:27:09.268Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl", hash = "sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4", size = 23382, upload-time = "2025-08-01T21:27:07.844Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.8.3"
|
||||
@ -68,43 +54,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.3"
|
||||
@ -156,50 +105,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "46.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a9/62/e3664e6ffd7743e1694b244dde70b43a394f6f7fbcacf7014a8ff5197c73/cryptography-46.0.1.tar.gz", hash = "sha256:ed570874e88f213437f5cf758f9ef26cbfc3f336d889b1e592ee11283bb8d1c7", size = 749198, upload-time = "2025-09-17T00:10:35.797Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/22/59/9ae689a25047e0601adfcb159ec4f83c0b4149fdb5c3030cc94cd218141d/cryptography-46.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0ff483716be32690c14636e54a1f6e2e1b7bf8e22ca50b989f88fa1b2d287080", size = 4308182, upload-time = "2025-09-17T00:08:39.388Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/ee/ca6cc9df7118f2fcd142c76b1da0f14340d77518c05b1ebfbbabca6b9e7d/cryptography-46.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9873bf7c1f2a6330bdfe8621e7ce64b725784f9f0c3a6a55c3047af5849f920e", size = 4572393, upload-time = "2025-09-17T00:08:41.663Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/a3/0f5296f63815d8e985922b05c31f77ce44787b3127a67c0b7f70f115c45f/cryptography-46.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0dfb7c88d4462a0cfdd0d87a3c245a7bc3feb59de101f6ff88194f740f72eda6", size = 4308400, upload-time = "2025-09-17T00:08:43.559Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/8c/74fcda3e4e01be1d32775d5b4dd841acaac3c1b8fa4d0774c7ac8d52463d/cryptography-46.0.1-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e22801b61613ebdebf7deb18b507919e107547a1d39a3b57f5f855032dd7cfb8", size = 4015786, upload-time = "2025-09-17T00:08:45.758Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/b8/85d23287baeef273b0834481a3dd55bbed3a53587e3b8d9f0898235b8f91/cryptography-46.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:757af4f6341ce7a1e47c326ca2a81f41d236070217e5fbbad61bbfe299d55d28", size = 4982606, upload-time = "2025-09-17T00:08:47.602Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/d3/de61ad5b52433b389afca0bc70f02a7a1f074651221f599ce368da0fe437/cryptography-46.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f7a24ea78de345cfa7f6a8d3bde8b242c7fac27f2bd78fa23474ca38dfaeeab9", size = 4604234, upload-time = "2025-09-17T00:08:49.879Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/1f/dbd4d6570d84748439237a7478d124ee0134bf166ad129267b7ed8ea6d22/cryptography-46.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e8776dac9e660c22241b6587fae51a67b4b0147daa4d176b172c3ff768ad736", size = 4307669, upload-time = "2025-09-17T00:08:52.321Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/fd/ca0a14ce7f0bfe92fa727aacaf2217eb25eb7e4ed513b14d8e03b26e63ed/cryptography-46.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9f40642a140c0c8649987027867242b801486865277cbabc8c6059ddef16dc8b", size = 4947579, upload-time = "2025-09-17T00:08:54.697Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/6b/09c30543bb93401f6f88fce556b3bdbb21e55ae14912c04b7bf355f5f96c/cryptography-46.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:449ef2b321bec7d97ef2c944173275ebdab78f3abdd005400cc409e27cd159ab", size = 4603669, upload-time = "2025-09-17T00:08:57.16Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/9a/38cb01cb09ce0adceda9fc627c9cf98eb890fc8d50cacbe79b011df20f8a/cryptography-46.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2dd339ba3345b908fa3141ddba4025568fa6fd398eabce3ef72a29ac2d73ad75", size = 4435828, upload-time = "2025-09-17T00:08:59.606Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/53/435b5c36a78d06ae0bef96d666209b0ecd8f8181bfe4dda46536705df59e/cryptography-46.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7411c910fb2a412053cf33cfad0153ee20d27e256c6c3f14d7d7d1d9fec59fd5", size = 4709553, upload-time = "2025-09-17T00:09:01.832Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/34/0ff0bb2d2c79f25a2a63109f3b76b9108a906dd2a2eb5c1d460b9938adbb/cryptography-46.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9babb7818fdd71394e576cf26c5452df77a355eac1a27ddfa24096665a27f8fd", size = 4293515, upload-time = "2025-09-17T00:09:12.861Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/b7/d4f848aee24ecd1be01db6c42c4a270069a4f02a105d9c57e143daf6cf0f/cryptography-46.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9f2c4cc63be3ef43c0221861177cee5d14b505cd4d4599a89e2cd273c4d3542a", size = 4545619, upload-time = "2025-09-17T00:09:15.397Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/a5/42fedefc754fd1901e2d95a69815ea4ec8a9eed31f4c4361fcab80288661/cryptography-46.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:41c281a74df173876da1dc9a9b6953d387f06e3d3ed9284e3baae3ab3f40883a", size = 4299160, upload-time = "2025-09-17T00:09:17.155Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/a1/cd21174f56e769c831fbbd6399a1b7519b0ff6280acec1b826d7b072640c/cryptography-46.0.1-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0a17377fa52563d730248ba1f68185461fff36e8bc75d8787a7dd2e20a802b7a", size = 3994491, upload-time = "2025-09-17T00:09:18.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/2f/a8cbfa1c029987ddc746fd966711d4fa71efc891d37fbe9f030fe5ab4eec/cryptography-46.0.1-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:0d1922d9280e08cde90b518a10cd66831f632960a8d08cb3418922d83fce6f12", size = 4960157, upload-time = "2025-09-17T00:09:20.923Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/ae/63a84e6789e0d5a2502edf06b552bcb0fa9ff16147265d5c44a211942abe/cryptography-46.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:af84e8e99f1a82cea149e253014ea9dc89f75b82c87bb6c7242203186f465129", size = 4577263, upload-time = "2025-09-17T00:09:23.356Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/8f/1b9fa8e92bd9cbcb3b7e1e593a5232f2c1e6f9bd72b919c1a6b37d315f92/cryptography-46.0.1-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ef648d2c690703501714588b2ba640facd50fd16548133b11b2859e8655a69da", size = 4298703, upload-time = "2025-09-17T00:09:25.566Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/af/bb95db070e73fea3fae31d8a69ac1463d89d1c084220f549b00dd01094a8/cryptography-46.0.1-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:e94eb5fa32a8a9f9bf991f424f002913e3dd7c699ef552db9b14ba6a76a6313b", size = 4926363, upload-time = "2025-09-17T00:09:27.451Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/3b/d8fb17ffeb3a83157a1cc0aa5c60691d062aceecba09c2e5e77ebfc1870c/cryptography-46.0.1-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:534b96c0831855e29fc3b069b085fd185aa5353033631a585d5cd4dd5d40d657", size = 4576958, upload-time = "2025-09-17T00:09:29.924Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/46/86bc3a05c10c8aa88c8ae7e953a8b4e407c57823ed201dbcba55c4d655f4/cryptography-46.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f9b55038b5c6c47559aa33626d8ecd092f354e23de3c6975e4bb205df128a2a0", size = 4422507, upload-time = "2025-09-17T00:09:32.222Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/4e/387e5a21dfd2b4198e74968a541cfd6128f66f8ec94ed971776e15091ac3/cryptography-46.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ec13b7105117dbc9afd023300fb9954d72ca855c274fe563e72428ece10191c0", size = 4683964, upload-time = "2025-09-17T00:09:34.118Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/3e/13ce6eab9ad6eba1b15a7bd476f005a4c1b3f299f4c2f32b22408b0edccf/cryptography-46.0.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9ed64e5083fa806709e74fc5ea067dfef9090e5b7a2320a49be3c9df3583a2d8", size = 4301110, upload-time = "2025-09-17T00:09:45.614Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/67/65dc233c1ddd688073cf7b136b06ff4b84bf517ba5529607c9d79720fc67/cryptography-46.0.1-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:341fb7a26bc9d6093c1b124b9f13acc283d2d51da440b98b55ab3f79f2522ead", size = 4562369, upload-time = "2025-09-17T00:09:47.601Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/db/d64ae4c6f4e98c3dac5bf35dd4d103f4c7c345703e43560113e5e8e31b2b/cryptography-46.0.1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6ef1488967e729948d424d09c94753d0167ce59afba8d0f6c07a22b629c557b2", size = 4302126, upload-time = "2025-09-17T00:09:49.335Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/19/5f1eea17d4805ebdc2e685b7b02800c4f63f3dd46cfa8d4c18373fea46c8/cryptography-46.0.1-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7823bc7cdf0b747ecfb096d004cc41573c2f5c7e3a29861603a2871b43d3ef32", size = 4009431, upload-time = "2025-09-17T00:09:51.239Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/b5/229ba6088fe7abccbfe4c5edb96c7a5ad547fac5fdd0d40aa6ea540b2985/cryptography-46.0.1-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:f736ab8036796f5a119ff8211deda416f8c15ce03776db704a7a4e17381cb2ef", size = 4980739, upload-time = "2025-09-17T00:09:54.181Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/9c/50aa38907b201e74bc43c572f9603fa82b58e831bd13c245613a23cff736/cryptography-46.0.1-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e46710a240a41d594953012213ea8ca398cd2448fbc5d0f1be8160b5511104a0", size = 4592289, upload-time = "2025-09-17T00:09:56.731Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/33/229858f8a5bb22f82468bb285e9f4c44a31978d5f5830bb4ea1cf8a4e454/cryptography-46.0.1-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:84ef1f145de5aee82ea2447224dc23f065ff4cc5791bb3b506615957a6ba8128", size = 4301815, upload-time = "2025-09-17T00:09:58.548Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/cb/b76b2c87fbd6ed4a231884bea3ce073406ba8e2dae9defad910d33cbf408/cryptography-46.0.1-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:9394c7d5a7565ac5f7d9ba38b2617448eba384d7b107b262d63890079fad77ca", size = 4943251, upload-time = "2025-09-17T00:10:00.475Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/0f/f66125ecf88e4cb5b8017ff43f3a87ede2d064cb54a1c5893f9da9d65093/cryptography-46.0.1-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ed957044e368ed295257ae3d212b95456bd9756df490e1ac4538857f67531fcc", size = 4591247, upload-time = "2025-09-17T00:10:02.874Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/22/9f3134ae436b63b463cfdf0ff506a0570da6873adb4bf8c19b8a5b4bac64/cryptography-46.0.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f7de12fa0eee6234de9a9ce0ffcfa6ce97361db7a50b09b65c63ac58e5f22fc7", size = 4428534, upload-time = "2025-09-17T00:10:04.994Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/39/e6042bcb2638650b0005c752c38ea830cbfbcbb1830e4d64d530000aa8dc/cryptography-46.0.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7fab1187b6c6b2f11a326f33b036f7168f5b996aedd0c059f9738915e4e8f53a", size = 4699541, upload-time = "2025-09-17T00:10:06.925Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docutils"
|
||||
version = "0.21.2"
|
||||
@ -225,18 +130,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/69/964b55f389c289e16ba2a5dfe587c3c462aac09e24123f09ddf703889584/furo-2025.9.25-py3-none-any.whl", hash = "sha256:2937f68e823b8e37b410c972c371bc2b1d88026709534927158e0cb3fac95afe", size = 340409, upload-time = "2025-09-25T21:37:17.244Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id"
|
||||
version = "1.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/11/102da08f88412d875fa2f1a9a469ff7ad4c874b0ca6fed0048fe385bdb3d/id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d", size = 15237, upload-time = "2024-12-04T19:53:05.575Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658", size = 13611, upload-time = "2024-12-04T19:53:03.02Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
@ -264,48 +157,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jaraco-classes"
|
||||
version = "3.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "more-itertools" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jaraco-context"
|
||||
version = "6.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", size = 13912, upload-time = "2024-08-20T03:39:27.358Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", size = 6825, upload-time = "2024-08-20T03:39:25.966Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jaraco-functools"
|
||||
version = "4.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "more-itertools" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f7/ed/1aa2d585304ec07262e1a83a9889880701079dde796ac7b1d1826f40c63d/jaraco_functools-4.3.0.tar.gz", hash = "sha256:cfd13ad0dd2c47a3600b439ef72d8615d482cedcff1632930d6f28924d92f294", size = 19755, upload-time = "2025-08-18T20:05:09.91Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/09/726f168acad366b11e420df31bf1c702a54d373a83f968d94141a8c3fde0/jaraco_functools-4.3.0-py3-none-any.whl", hash = "sha256:227ff8ed6f7b8f62c56deff101545fa7543cf2c8e7b82a7c2116e672f29c26e8", size = 10408, upload-time = "2025-08-18T20:05:08.69Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jeepney"
|
||||
version = "0.9.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.6"
|
||||
@ -318,23 +169,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keyring"
|
||||
version = "25.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "jaraco-classes" },
|
||||
{ name = "jaraco-context" },
|
||||
{ name = "jaraco-functools" },
|
||||
{ name = "jeepney", marker = "sys_platform == 'linux'" },
|
||||
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
|
||||
{ name = "secretstorage", marker = "sys_platform == 'linux'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/70/09/d904a6e96f76ff214be59e7aa6ef7190008f52a0ab6689760a98de0bf37d/keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66", size = 62750, upload-time = "2024-12-25T15:26:45.782Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/32/da7f44bcb1105d3e88a0b74ebdca50c59121d2ddf71c9e34ba47df7f3a56/keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd", size = 39085, upload-time = "2024-12-25T15:26:44.377Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown-it-py"
|
||||
version = "3.0.0"
|
||||
@ -406,15 +240,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "more-itertools"
|
||||
version = "10.8.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "myst-parser"
|
||||
version = "4.0.1"
|
||||
@ -432,39 +257,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nh3"
|
||||
version = "0.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c3/a4/96cff0977357f60f06ec4368c4c7a7a26cccfe7c9fcd54f5378bf0428fd3/nh3-0.3.0.tar.gz", hash = "sha256:d8ba24cb31525492ea71b6aac11a4adac91d828aadeff7c4586541bf5dc34d2f", size = 19655, upload-time = "2025-07-17T14:43:37.05Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/11/340b7a551916a4b2b68c54799d710f86cf3838a4abaad8e74d35360343bb/nh3-0.3.0-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a537ece1bf513e5a88d8cff8a872e12fe8d0f42ef71dd15a5e7520fecd191bbb", size = 1427992, upload-time = "2025-07-17T14:43:06.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/7f/7c6b8358cf1222921747844ab0eef81129e9970b952fcb814df417159fb9/nh3-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c915060a2c8131bef6a29f78debc29ba40859b6dbe2362ef9e5fd44f11487c2", size = 798194, upload-time = "2025-07-17T14:43:08.263Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/da/c5fd472b700ba37d2df630a9e0d8cc156033551ceb8b4c49cc8a5f606b68/nh3-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba0caa8aa184196daa6e574d997a33867d6d10234018012d35f86d46024a2a95", size = 837884, upload-time = "2025-07-17T14:43:09.233Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/3c/cba7b26ccc0ef150c81646478aa32f9c9535234f54845603c838a1dc955c/nh3-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:80fe20171c6da69c7978ecba33b638e951b85fb92059259edd285ff108b82a6d", size = 996365, upload-time = "2025-07-17T14:43:10.243Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/ba/59e204d90727c25b253856e456ea61265ca810cda8ee802c35f3fadaab00/nh3-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e90883f9f85288f423c77b3f5a6f4486375636f25f793165112679a7b6363b35", size = 1071042, upload-time = "2025-07-17T14:43:11.57Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/71/2fb1834c10fab6d9291d62c95192ea2f4c7518bd32ad6c46aab5d095cb87/nh3-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0649464ac8eee018644aacbc103874ccbfac80e3035643c3acaab4287e36e7f5", size = 995737, upload-time = "2025-07-17T14:43:12.659Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/c1/8f8ccc2492a000b6156dce68a43253fcff8b4ce70ab4216d08f90a2ac998/nh3-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1adeb1062a1c2974bc75b8d1ecb014c5fd4daf2df646bbe2831f7c23659793f9", size = 980552, upload-time = "2025-07-17T14:43:13.763Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/d6/f1c6e091cbe8700401c736c2bc3980c46dca770a2cf6a3b48a175114058e/nh3-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:7275fdffaab10cc5801bf026e3c089d8de40a997afc9e41b981f7ac48c5aa7d5", size = 593618, upload-time = "2025-07-17T14:43:15.098Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/1e/80a8c517655dd40bb13363fc4d9e66b2f13245763faab1a20f1df67165a7/nh3-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:423201bbdf3164a9e09aa01e540adbb94c9962cc177d5b1cbb385f5e1e79216e", size = 598948, upload-time = "2025-07-17T14:43:16.064Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9a/e0/af86d2a974c87a4ba7f19bc3b44a8eaa3da480de264138fec82fe17b340b/nh3-0.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:16f8670201f7e8e0e05ed1a590eb84bfa51b01a69dd5caf1d3ea57733de6a52f", size = 580479, upload-time = "2025-07-17T14:43:17.038Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/e0/cf1543e798ba86d838952e8be4cb8d18e22999be2a24b112a671f1c04fd6/nh3-0.3.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ec6cfdd2e0399cb79ba4dcffb2332b94d9696c52272ff9d48a630c5dca5e325a", size = 1442218, upload-time = "2025-07-17T14:43:18.087Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/86/a96b1453c107b815f9ab8fac5412407c33cc5c7580a4daf57aabeb41b774/nh3-0.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5e7185599f89b0e391e2f29cc12dc2e206167380cea49b33beda4891be2fe1", size = 823791, upload-time = "2025-07-17T14:43:19.721Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/33/11e7273b663839626f714cb68f6eb49899da5a0d9b6bc47b41fe870259c2/nh3-0.3.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:389d93d59b8214d51c400fb5b07866c2a4f79e4e14b071ad66c92184fec3a392", size = 811143, upload-time = "2025-07-17T14:43:20.779Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/1b/b15bd1ce201a1a610aeb44afd478d55ac018b4475920a3118ffd806e2483/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e9e6a7e4d38f7e8dda9edd1433af5170c597336c1a74b4693c5cb75ab2b30f2a", size = 1064661, upload-time = "2025-07-17T14:43:21.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/14/079670fb2e848c4ba2476c5a7a2d1319826053f4f0368f61fca9bb4227ae/nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7852f038a054e0096dac12b8141191e02e93e0b4608c4b993ec7d4ffafea4e49", size = 997061, upload-time = "2025-07-17T14:43:23.179Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/e5/ac7fc565f5d8bce7f979d1afd68e8cb415020d62fa6507133281c7d49f91/nh3-0.3.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af5aa8127f62bbf03d68f67a956627b1bd0469703a35b3dad28d0c1195e6c7fb", size = 924761, upload-time = "2025-07-17T14:43:24.23Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/2c/6394301428b2017a9d5644af25f487fa557d06bc8a491769accec7524d9a/nh3-0.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f416c35efee3e6a6c9ab7716d9e57aa0a49981be915963a82697952cba1353e1", size = 803959, upload-time = "2025-07-17T14:43:26.377Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/9a/344b9f9c4bd1c2413a397f38ee6a3d5db30f1a507d4976e046226f12b297/nh3-0.3.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:37d3003d98dedca6cd762bf88f2e70b67f05100f6b949ffe540e189cc06887f9", size = 844073, upload-time = "2025-07-17T14:43:27.375Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/3f/cd37f76c8ca277b02a84aa20d7bd60fbac85b4e2cbdae77cb759b22de58b/nh3-0.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:634e34e6162e0408e14fb61d5e69dbaea32f59e847cfcfa41b66100a6b796f62", size = 1000680, upload-time = "2025-07-17T14:43:28.452Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/db/7aa11b44bae4e7474feb1201d8dee04fabe5651c7cb51409ebda94a4ed67/nh3-0.3.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:b0612ccf5de8a480cf08f047b08f9d3fecc12e63d2ee91769cb19d7290614c23", size = 1076613, upload-time = "2025-07-17T14:43:30.031Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/03/03f79f7e5178eb1ad5083af84faff471e866801beb980cc72943a4397368/nh3-0.3.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c7a32a7f0d89f7d30cb8f4a84bdbd56d1eb88b78a2434534f62c71dac538c450", size = 1001418, upload-time = "2025-07-17T14:43:31.429Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/55/1974bcc16884a397ee699cebd3914e1f59be64ab305533347ca2d983756f/nh3-0.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3f1b4f8a264a0c86ea01da0d0c390fe295ea0bcacc52c2103aca286f6884f518", size = 986499, upload-time = "2025-07-17T14:43:32.459Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/50/76936ec021fe1f3270c03278b8af5f2079038116b5d0bfe8538ffe699d69/nh3-0.3.0-cp38-abi3-win32.whl", hash = "sha256:6d68fa277b4a3cf04e5c4b84dd0c6149ff7d56c12b3e3fab304c525b850f613d", size = 599000, upload-time = "2025-07-17T14:43:33.852Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/ae/324b165d904dc1672eee5f5661c0a68d4bab5b59fbb07afb6d8d19a30b45/nh3-0.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:bae63772408fd63ad836ec569a7c8f444dd32863d0c67f6e0b25ebbd606afa95", size = 604530, upload-time = "2025-07-17T14:43:34.95Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/76/3165e84e5266d146d967a6cc784ff2fbf6ddd00985a55ec006b72bc39d5d/nh3-0.3.0-cp38-abi3-win_arm64.whl", hash = "sha256:d97d3efd61404af7e5721a0e74d81cdbfc6e5f97e11e731bb6d090e30a7b62b2", size = 585971, upload-time = "2025-07-17T14:43:35.936Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
@ -483,15 +275,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.23"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
@ -501,15 +284,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyproject-hooks"
|
||||
version = "1.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "8.4.2"
|
||||
@ -526,15 +300,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pywin32-ctypes"
|
||||
version = "0.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyxdg"
|
||||
version = "0.28"
|
||||
@ -590,20 +355,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "readme-renderer"
|
||||
version = "44.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "docutils" },
|
||||
{ name = "nh3" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.5"
|
||||
@ -619,40 +370,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests-toolbelt"
|
||||
version = "1.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc3986"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "14.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "markdown-it-py" },
|
||||
{ name = "pygments" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/75/af448d8e52bf1d8fa6a9d089ca6c07ff4453d86c65c145d0a300bb073b9b/rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8", size = 224441, upload-time = "2025-07-25T07:32:58.125Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/30/3c4d035596d3cf444529e0b2953ad0466f6049528a879d27534700580395/rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f", size = 243368, upload-time = "2025-07-25T07:32:56.73Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roman-numerals-py"
|
||||
version = "3.1.0"
|
||||
@ -662,19 +379,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742, upload-time = "2025-02-22T07:34:52.422Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secretstorage"
|
||||
version = "3.4.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cryptography" },
|
||||
{ name = "jeepney" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/31/9f/11ef35cf1027c1339552ea7bfe6aaa74a8516d8b5caf6e7d338daf54fd80/secretstorage-3.4.0.tar.gz", hash = "sha256:c46e216d6815aff8a8a18706a2fbfd8d53fcbb0dce99301881687a1b0289ef7c", size = 19748, upload-time = "2025-09-09T16:42:13.859Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/91/ff/2e2eed29e02c14a5cb6c57f09b2d5b40e65d6cc71f45b52e0be295ccbc2f/secretstorage-3.4.0-py3-none-any.whl", hash = "sha256:0e3b6265c2c63509fb7415717607e4b2c9ab767b7f344a57473b779ca13bd02e", size = 15272, upload-time = "2025-09-09T16:42:12.744Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "80.9.0"
|
||||
@ -825,6 +529,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "symconf"
|
||||
version = "0.8.2"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "colorama" },
|
||||
@ -832,55 +537,29 @@ dependencies = [
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
build = [
|
||||
{ name = "build" },
|
||||
{ name = "twine" },
|
||||
dev = [
|
||||
{ name = "pytest" },
|
||||
]
|
||||
docs = [
|
||||
doc = [
|
||||
{ name = "furo" },
|
||||
{ name = "myst-parser" },
|
||||
{ name = "sphinx" },
|
||||
{ name = "sphinx-autodoc-typehints" },
|
||||
{ name = "sphinx-togglebutton" },
|
||||
]
|
||||
tests = [
|
||||
{ name = "pytest" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "build", marker = "extra == 'build'" },
|
||||
{ name = "colorama" },
|
||||
{ name = "furo", marker = "extra == 'docs'" },
|
||||
{ name = "myst-parser", marker = "extra == 'docs'" },
|
||||
{ name = "pytest", marker = "extra == 'tests'" },
|
||||
{ name = "furo", marker = "extra == 'doc'" },
|
||||
{ name = "myst-parser", marker = "extra == 'doc'" },
|
||||
{ name = "pytest", marker = "extra == 'dev'" },
|
||||
{ name = "pyxdg" },
|
||||
{ name = "sphinx", marker = "extra == 'docs'" },
|
||||
{ name = "sphinx-autodoc-typehints", marker = "extra == 'docs'" },
|
||||
{ name = "sphinx-togglebutton", marker = "extra == 'docs'" },
|
||||
{ name = "twine", marker = "extra == 'build'" },
|
||||
]
|
||||
provides-extras = ["tests", "docs", "build"]
|
||||
|
||||
[[package]]
|
||||
name = "twine"
|
||||
version = "6.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "id" },
|
||||
{ name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" },
|
||||
{ name = "packaging" },
|
||||
{ name = "readme-renderer" },
|
||||
{ name = "requests" },
|
||||
{ name = "requests-toolbelt" },
|
||||
{ name = "rfc3986" },
|
||||
{ name = "rich" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" },
|
||||
{ name = "sphinx", marker = "extra == 'doc'" },
|
||||
{ name = "sphinx-autodoc-typehints", marker = "extra == 'doc'" },
|
||||
{ name = "sphinx-togglebutton", marker = "extra == 'doc'" },
|
||||
]
|
||||
provides-extras = ["doc", "dev"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
|
Loading…
x
Reference in New Issue
Block a user