From e0032c2bf8d0c35e6efdb917b8b1d386d56930f6 Mon Sep 17 00:00:00 2001 From: Kyle Finley Date: Wed, 27 Dec 2023 21:25:12 -0500 Subject: [PATCH] add mixins and some utils (#19) --- .github/workflows/default.yml | 13 +- .github/workflows/on-release.yml | 57 + .github/workflows/release-management.yml | 2 +- .../{spellcheck.yml => spell-check.yml} | 2 - .pre-commit-config.yaml | 16 +- .readthedocs.yml | 1 + Makefile | 10 +- docs/source/_static/scripts/custom.js | 4 + docs/source/conf.py | 236 ++- f_lib/__init__.py | 19 +- f_lib/_environment.py | 108 ++ f_lib/_os_info.py | 109 ++ f_lib/_system_info.py | 93 ++ f_lib/aws/aws_lambda/type_defs.py | 102 +- f_lib/constants.py | 5 + f_lib/mixins/__init__.py | 8 + f_lib/mixins/_cli_interface.py | 191 +++ f_lib/mixins/_del_cached_prop.py | 19 + f_lib/py.typed | 0 f_lib/utils/__init__.py | 43 + package-lock.json | 1330 ++++++++--------- package.json | 7 +- poetry.lock | 402 +++-- poetry.toml | 2 + pyproject.toml | 150 +- tests/conftest.py | 27 + tests/test_placeholder.py | 5 - tests/unit/__init__.py | 1 + tests/unit/conftest.py | 37 + tests/unit/mixins/__init__.py | 1 + tests/unit/mixins/test__cli_interface.py | 204 +++ tests/unit/mixins/test__del_cached_prop.py | 37 + tests/unit/test__environment.py | 99 ++ tests/unit/test__os_info.py | 131 ++ tests/unit/test__system_info.py | 116 ++ tests/unit/utils/__init__.py | 1 + tests/unit/utils/test___init__.py | 78 + 37 files changed, 2541 insertions(+), 1125 deletions(-) create mode 100644 .github/workflows/on-release.yml rename .github/workflows/{spellcheck.yml => spell-check.yml} (99%) create mode 100644 docs/source/_static/scripts/custom.js create mode 100644 f_lib/_environment.py create mode 100644 f_lib/_os_info.py create mode 100644 f_lib/_system_info.py create mode 100644 f_lib/constants.py create mode 100644 f_lib/mixins/__init__.py create mode 100644 f_lib/mixins/_cli_interface.py create mode 100644 f_lib/mixins/_del_cached_prop.py create mode 100644 f_lib/py.typed create mode 100644 f_lib/utils/__init__.py create mode 100644 poetry.toml create mode 100644 tests/conftest.py delete mode 100644 tests/test_placeholder.py create mode 100644 tests/unit/__init__.py create mode 100644 tests/unit/conftest.py create mode 100644 tests/unit/mixins/__init__.py create mode 100644 tests/unit/mixins/test__cli_interface.py create mode 100644 tests/unit/mixins/test__del_cached_prop.py create mode 100644 tests/unit/test__environment.py create mode 100644 tests/unit/test__os_info.py create mode 100644 tests/unit/test__system_info.py create mode 100644 tests/unit/utils/__init__.py create mode 100644 tests/unit/utils/test___init__.py diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index be15bcd..d220a25 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -11,10 +11,10 @@ defaults: shell: bash env: + DEFAULT_PYTHON_VERSION: 3.11 NODE_VERSION: '20' PYTEST_ADDOPTS: --color=yes - jobs: pre-commit: name: pre-commit @@ -32,7 +32,7 @@ jobs: name: Lint Python strategy: matrix: - python-version: [3.9, '3.10', 3.11] + python-version: [3.11, 3.12] runs-on: ubuntu-latest steps: - name: Checkout Repo @@ -53,7 +53,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.9, '3.10', 3.11] + python-version: [3.11, 3.12] steps: - uses: actions/checkout@v3 - uses: ITProKyle/action-setup-python@v0.4.0 @@ -68,9 +68,6 @@ jobs: - lint-python - test-python runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.11] steps: - name: Checkout Repo (complete) uses: actions/checkout@v3 @@ -78,7 +75,9 @@ jobs: fetch-depth: 0 - uses: ITProKyle/action-setup-python@v0.4.0 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ env.DEFAULT_PYTHON_VERSION }} + - name: Install Poetry Plugins + run: poetry self add "poetry-dynamic-versioning[plugin]" - name: Run Build run: make build - name: Upload Distribution Artifact diff --git a/.github/workflows/on-release.yml b/.github/workflows/on-release.yml new file mode 100644 index 0000000..e7751e9 --- /dev/null +++ b/.github/workflows/on-release.yml @@ -0,0 +1,57 @@ +name: Publish Release + +on: + release: + types: + - published + +env: + DEFAULT_PYTHON_VERSION: 3.11 + +jobs: + build-pypi: + name: Build PyPi 📦 + runs-on: ubuntu-latest + steps: + - name: Checkout Repo (complete) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: ITProKyle/action-setup-python@v0.4.0 + with: + poetry-install: false + python-version: ${{ env.DEFAULT_PYTHON_VERSION }} + - name: Install Poetry Plugins + run: poetry self add "poetry-dynamic-versioning[plugin]" + - name: Run Build + run: make build + - name: Upload Distribution Artifact + uses: actions/upload-artifact@v4 + with: + name: pypi-dist + path: dist + publish-pypi: + name: Publish 📦 To PyPI + needs: + - build-pypi + runs-on: ubuntu-latest + steps: + - name: Checkout Repo (complete) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Download Distribution Artifact + uses: actions/download-artifact@v4 + with: + name: pypi-dist + path: dist + - uses: ITProKyle/action-setup-python@v0.4.0 + with: + poetry-install: false + python-version: ${{ env.DEFAULT_PYTHON_VERSION }} + - name: Install Poetry Plugins + run: poetry self add "poetry-dynamic-versioning[plugin]" + - name: Publish Distribution 📦 to PyPI + env: + POETRY_PYPI_TOKEN_PYPI: ${{ secrets.pypi_password }} + run: poetry publish diff --git a/.github/workflows/release-management.yml b/.github/workflows/release-management.yml index e60096f..be33339 100644 --- a/.github/workflows/release-management.yml +++ b/.github/workflows/release-management.yml @@ -7,7 +7,7 @@ on: - master jobs: - update_draft_release: + update-draft-release: name: Draft release runs-on: ubuntu-latest steps: diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spell-check.yml similarity index 99% rename from .github/workflows/spellcheck.yml rename to .github/workflows/spell-check.yml index d6158c7..a7f7ff9 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spell-check.yml @@ -5,11 +5,9 @@ name: Spell Check on: push - env: NODE_VERSION: '20' - jobs: spell-check: runs-on: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index df770a8..e9e4268 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,16 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-json - id: check-merge-conflict - id: check-yaml - args: - - --unsafe # needed for parsing CFN - id: end-of-file-fixer - exclude: .*\.json-result - id: file-contents-sorter args: [--unique] files: | (?x)^( + \.dockerignore| \.gitignore )$ - id: pretty-format-json @@ -30,6 +28,14 @@ repos: - id: trailing-whitespace - id: mixed-line-ending args: [--fix=lf] + - repo: https://github.com/pappasam/toml-sort + rev: v0.23.1 + hooks: + - id: toml-sort-fix + exclude: | + (?x)^( + (.*/)?poetry\.lock + )$ - repo: https://github.com/ITProKyle/pre-commit-hook-yamlfmt rev: v0.2.1 hooks: @@ -44,7 +50,7 @@ repos: - mdformat-gfm - mdformat-toc - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.37.0 + rev: v0.38.0 hooks: - id: markdownlint language_version: lts diff --git a/.readthedocs.yml b/.readthedocs.yml index 8709939..d4b8ccb 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -11,6 +11,7 @@ build: - pip install poetry - poetry config virtualenvs.create false post_install: + - poetry self add "poetry-dynamic-versioning[plugin]" - poetry install --with docs # Build documentation in the docs/ directory with Sphinx diff --git a/Makefile b/Makefile index bab5dc1..29778b1 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ help: ## show this message build: ## build the PyPi release @poetry build +.PHONY: docs docs: ## delete build artifacts, start sphinx-autobuild server, & open browser window @if [[ -z "$${CI}" ]]; then \ $(MAKE) --no-print-directory -C docs docs; \ @@ -93,5 +94,10 @@ spellcheck: ## run cspell --relative \ --show-context -test: ## run tests - @echo "no tests configured for this project yet" +test: ## run integration and unit tests + @echo "Running integration & unit tests..." + @poetry run pytest $(PYTEST_REPORT_OPTS) \ + --cov f_lib \ + --cov-report term-missing:skip-covered \ + --dist loadfile \ + --numprocesses auto diff --git a/docs/source/_static/scripts/custom.js b/docs/source/_static/scripts/custom.js new file mode 100644 index 0000000..ae7a684 --- /dev/null +++ b/docs/source/_static/scripts/custom.js @@ -0,0 +1,4 @@ +// open external links in new tabs +$(document).ready(function () { + $('a.external').attr('target', '_blank'); +}); diff --git a/docs/source/conf.py b/docs/source/conf.py index 818e6fc..ebb67a6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,9 +1,10 @@ """Configuration file for the Sphinx documentation builder. -This file only contain a selection of the most common options. For a -full list see the documentation: http://www.sphinx-doc.org/en/master/config +https://www.sphinx-doc.org/en/master/usage/configuration.html """ +import os +from datetime import date from pathlib import Path from pkg_resources import get_distribution @@ -11,184 +12,111 @@ ROOT_DIR = Path(__file__).parent.parent.parent DOC_SRC = ROOT_DIR / "docs" / "source" -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# from os.path import dirname, realpath -# import os.path -# import sys -# root_dir = dirname(dirname(dirname(realpath(__file__)))) -# sys.path.insert(0, os.path.join(root_dir, 'src')) -# -# from local_package import __version__ - - # -- Project information ----------------------------------------------------- project = "f-lib" -copyright = "2019, Kyle Finley" # noqa: A001 +copyright = f"{date.today().year}, Kyle Finley" # noqa: A001, DTZ011 author = "Kyle Finley" -release = get_distribution(project).version -version = release +version = get_distribution(project).version +release = ".".join(version.split(".")[:2]) # short X.Y version # -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. - +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration +add_function_parentheses = True +add_module_names = True +default_role = None +exclude_patterns = [] extensions = [ "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", "sphinx.ext.napoleon", + "sphinx.ext.todo", "sphinx.ext.viewcode", + "sphinx_copybutton", + "sphinx_design", "sphinxcontrib.apidoc", + "sphinxcontrib.jquery", ] - -# The file extensions of source files. Sphinx considers the files with -# this suffix as sources. The value can be a dictionary mapping file -# extensions to file types. - -source_suffix = { - ".rst": "restructuredtext", - ".txt": "restructuredtext", - ".md": "markdown", +highlight_language = "default" +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), # link to python docs } - - -# The document name of the “master” document, that is, the document that -# contains the root toctree directive. - +language = "en" master_doc = "index" - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. - -exclude_patterns = [] - -# A list of paths that contain extra templates (or templates that overwrite -# builtin/theme-specific templates). Relative paths are taken as relative -# to the configuration directory. - -templates_path = ["_templates"] - -# The style name to use for Pygments highlighting of source code. If not -# set, either the themes default style or 'sphinx' is selected for HTML -# output. - +needs_extensions = {} +nitpicky = True +primary_domain = "py" pygments_style = "one-dark" # syntax highlighting style pygments_dark_style = "one-dark" # syntax highlighting style +source_suffix = {".rst": "restructuredtext"} +templates_path = ["_templates"] # template dir relative to this dir # -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. - -html_theme = "sphinx_rtd_theme" - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# A list of CSS files. The entry must be a filename string or a tuple -# containing the filename string and the attributes dictionary. The -# filename must be relative to the html_static_path, or a full URI with -# scheme like http://example.org/style.css. The attributes is used for -# attributes of tag. It defaults to an empty list. - -html_css_files = [] - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". - -html_static_path = ["_static"] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = "pydoc" - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = {} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, f"{project}.tex", f"{project} Documentation", author, "manual"), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [(master_doc, project, f"{project} Documentation", [author], 1)] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ( - master_doc, - project, - f"{project} Documentation", - author, - project, - "One line description of project.", - "Miscellaneous", - ), +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output +html_codeblock_linenos_style = "inline" +html_css_files = [ # files relative to html_static_path + "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/fontawesome.min.css", + "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/solid.min.css", + "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/brands.min.css", ] +html_short_title = f"{project} v{release}" +html_show_copyright = True +html_show_sphinx = False +html_static_path = ["_static"] # dir with static files relative to this dir +html_theme = "furo" # theme to use for HTML and HTML Help pages +html_theme_options = { + "dark_css_variables": { + "font-stack--monospace": "Inconsolata, monospace", + }, + "light_css_variables": { + "font-stack--monospace": "Inconsolata, monospace", + }, +} +html_title = f"{project} v{version}" -# -- Options for Epub output ------------------------------------------------- +# -- Options for sphinx-apidoc ----------------------------------------------- +# https://www.sphinx-doc.org/en/master/man/sphinx-apidoc.html#environment +os.environ["SPHINX_APIDOC_OPTIONS"] = "members" -# Bibliographic Dublin Core info. -epub_title = project -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' +# -- Options of sphinx.ext.autodoc ------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#configuration +autoclass_content = "class" +autodoc_class_signature = "separated" +autodoc_default_options = { + "exclude-members": ", ".join(["model_config", "model_fields"]), # noqa: FLY002 + "member-order": "bysource", + "members": True, + "show-inheritance": True, +} +autodoc_preserve_defaults = True +autodoc_type_aliases = {} +autodoc_typehints = "signature" -# A unique identification for the text. -# -# epub_uid = '' -# A list of files that should not be packed into the epub file. -epub_exclude_files = ["search.html"] +# -- Options for sphinx.ext.napoleon ---------------------------------------- +# https://www.sphinx-doc.org/en/3.x/usage/extensions/napoleon.html#configuration +napoleon_attr_annotations = True +napoleon_google_docstring = True +napoleon_include_init_with_doc = False +napoleon_include_private_with_doc = False +napoleon_include_special_with_doc = True +napoleon_type_aliases = autodoc_type_aliases +napoleon_use_admonition_for_examples = False +napoleon_use_admonition_for_notes = False +napoleon_use_admonition_for_references = False +napoleon_use_ivar = False +napoleon_use_param = False +napoleon_use_rtype = True -# -- Options for autodoc ----------------------------------------------------- -autoclass_content = "both" +# -- Options for sphinx.ext.todo --------------------------------------------- +# https://www.sphinx-doc.org/page/usage/extensions/todo.html +todo_include_todos = True -# -- Options for napoleon ---------------------------------------------------- -napoleon_google_docstring = True -napoleon_include_init_with_doc = False # -- Options for sphinxcontrib.apidoc ---------------------------------------- apidoc_excluded_paths = [] @@ -198,3 +126,11 @@ apidoc_output_dir = "apidocs" apidoc_separate_modules = True apidoc_toc_file = "index" + + +# -- Options for sphinx_copybutton --------------------------------- +# https://sphinx-copybutton.readthedocs.io/en/latest/index.html +copybutton_prompt_text = r">>> |\.\.\. |\$ |In \[\d*\]: | {2,5}\.\.\.: | {5,8}: " +copybutton_prompt_is_regexp = True +copybutton_remove_prompts = True +copybutton_line_continuation_character = "\\" diff --git a/f_lib/__init__.py b/f_lib/__init__.py index 269c070..e6be40b 100644 --- a/f_lib/__init__.py +++ b/f_lib/__init__.py @@ -1,8 +1,25 @@ """Import modules.""" from importlib.metadata import PackageNotFoundError, version +from . import aws, constants, mixins, utils +from ._environment import Environment +from ._os_info import OsInfo +from ._system_info import SystemInfo, UnknownPlatformArchitectureError + try: __version__ = version(__name__) -except PackageNotFoundError: +except PackageNotFoundError: # cov: ignore # package is not installed __version__ = "0.0.0" + + +__all__ = [ + "Environment", + "OsInfo", + "SystemInfo", + "aws", + "constants", + "mixins", + "utils", + "UnknownPlatformArchitectureError", +] diff --git a/f_lib/_environment.py b/f_lib/_environment.py new file mode 100644 index 0000000..2a9bf7a --- /dev/null +++ b/f_lib/_environment.py @@ -0,0 +1,108 @@ +"""Environment object class.""" +import json +import logging +import os +from pathlib import Path +from typing import Self + +from ._system_info import SystemInfo + +LOGGER = logging.getLogger(__name__) + + +class Environment: + """Object to simplify getting information about the runtime environment.""" + + root_dir: Path + """Root directory of the environment.""" + + sys: SystemInfo + """Information about the system.""" + + def __init__( + self, + *, + environ: dict[str, str] | None = None, + root_dir: Path | None = None, + ) -> None: + """Instantiate class. + + Args: + environ: Environment variables. + root_dir: Root directory of the environment (e.g. :meth:`pathlib.Path.cwd`). + + """ + self.root_dir = root_dir or Path.cwd() + self.sys = SystemInfo() + self.vars = environ if isinstance(environ, dict) else os.environ.copy() + + @property + def ci(self) -> bool: + """Return CI status. + + Returns: + bool + + """ + return "CI" in self.vars + + @ci.setter + def ci(self, value: object) -> None: + """Set the value of CI.""" + if value: + self._update_vars({"CI": "1"}) + else: + self.vars.pop("CI", None) + + @ci.deleter + def ci(self) -> None: + """Delete the value of CI.""" + self.vars.pop("CI", None) + + @property + def debug(self) -> bool: + """Get debug setting from the environment.""" + return "DEBUG" in self.vars + + @debug.setter + def debug(self, value: object) -> None: + """Set the value of DEBUG.""" + if value: + self._update_vars({"DEBUG": "1"}) + else: + self.vars.pop("DEBUG", None) + + @property + def verbose(self) -> bool: + """Get verbose setting from the environment.""" + return "VERBOSE" in self.vars + + @verbose.setter + def verbose(self, value: object) -> None: + """Set the value of VERBOSE.""" + if value: + self._update_vars({"VERBOSE": "1"}) + else: + self.vars.pop("VERBOSE", None) + + def copy(self: Self) -> Self: + """Copy the contents of this object into a new instance. + + Returns: + New instance with the same contents. + + """ + return self.__class__( + environ=self.vars.copy(), + root_dir=self.root_dir, + ) + + def _update_vars(self, env_vars: dict[str, str]) -> None: + """Update vars and log the change. + + Args: + env_vars (Dict[str, str]): Dict to update self.vars with. + + """ + self.vars.update(env_vars) + LOGGER.debug("updated environment variables: %s", json.dumps(env_vars)) diff --git a/f_lib/_os_info.py b/f_lib/_os_info.py new file mode 100644 index 0000000..ad700c0 --- /dev/null +++ b/f_lib/_os_info.py @@ -0,0 +1,109 @@ +"""Operating system information.""" +from __future__ import annotations + +import os +import platform +from functools import cached_property +from pathlib import Path +from typing import TYPE_CHECKING, ClassVar, cast, final + +from platformdirs.unix import Unix +from platformdirs.windows import Windows + +if TYPE_CHECKING: + import platformdirs + + +@final +class OsInfo: + """Information about the operating system running on the current system.""" + + __instance: ClassVar[OsInfo | None] = None + + def __new__(cls, *args: object, **kwargs: object) -> OsInfo: + """Create a new instance of class. + + This class is a singleton so it will always return the same instance. + + """ + if cls.__instance is None: + cls.__instance = cast(OsInfo, super().__new__(cls, *args, **kwargs)) + return cls.__instance + + @cached_property + def _platform_dirs(self) -> platformdirs.PlatformDirsABC: + """Instance of platformdirs class to get platform specific directories. + + ``appname``, ``appauthor``, and ``version`` are not passed to the class. + This is so that the base directory is returned. + + """ + if self.is_windows: + return Windows(appname="f-lib", appauthor="finley") + # platformdirs does no handle macOS the way I would like it to so alway use unix + return Unix(appname="f-lib", appauthor="finley") + + @cached_property + def is_darwin(self) -> bool: + """Operating system is Darwin.""" + return self.name == "darwin" + + @cached_property + def is_linux(self) -> bool: + """Operating system is Linux.""" + return self.name == "linux" + + @cached_property + def is_macos(self) -> bool: + """Operating system is macOS. + + Does not differentiate between macOS and Darwin. + + """ + return self.is_darwin + + @cached_property + def is_posix(self) -> bool: + """Operating system is posix.""" + return os.name == "posix" + + @cached_property + def is_windows(self) -> bool: + """Operating system is Windows.""" + return self.name in ("windows", "mingw64", "msys_nt", "cygwin_nt") + + @cached_property + def name(self) -> str: + """Operating system name set to lowercase for consistency.""" + return platform.system().lower() + + @cached_property + def user_config_dir(self) -> Path: + """Path to the config directory for the user. + + - ``~/.config`` + - ``%USERPROFILE%/AppData/Local`` + - ``%USERPROFILE%/AppData/Roaming`` + + """ + return Path(self._platform_dirs.user_config_dir) + + @cached_property + def user_data_dir(self) -> Path: + """Path to data directory tied to the user. + + - ``~/.local/share`` + - ``%USERPROFILE%/AppData/Local`` + - ``%USERPROFILE%/AppData/Roaming`` + + """ + return Path(self._platform_dirs.user_data_dir) + + @classmethod + def clear_singleton(cls) -> None: + """Clear singleton instances. + + Intended to only be used for running tests. + + """ + cls.__instance = None diff --git a/f_lib/_system_info.py b/f_lib/_system_info.py new file mode 100644 index 0000000..3498469 --- /dev/null +++ b/f_lib/_system_info.py @@ -0,0 +1,93 @@ +"""System information.""" +from __future__ import annotations + +import platform +import sys +from functools import cached_property +from typing import ClassVar, Literal, cast, final + +from ._os_info import OsInfo + + +class UnknownPlatformArchitectureError(Exception): + """Raised when the platform architecture can't be determined.""" + + def __init__(self, value: str) -> None: + """Instantiate class. + + Args: + value: The value causing the architecture to be unknown. + + """ + super().__init__(f"Unknown system architecture detected! ({value})") + + +@final +class SystemInfo: + """Information about the system running the application.""" + + __instance: ClassVar[SystemInfo | None] = None + + def __new__(cls, *args: object, **kwargs: object) -> SystemInfo: + """Create a new instance of class. + + This class is a singleton so it will always return the same instance. + + """ + if cls.__instance is None: + cls.__instance = cast(SystemInfo, super().__new__(cls, *args, **kwargs)) + return cls.__instance + + @cached_property + def architecture(self) -> Literal["amd32", "amd64", "arm32", "arm64"]: + """System's CPU architecture.""" + if self.is_arm: + if self.is_64bit: + return "arm64" + return "arm32" + if self.is_x86: + if self.is_64bit: + return "amd64" + return "amd32" + raise UnknownPlatformArchitectureError(platform.machine()) + + @cached_property + def is_32bit(self) -> bool: + """Whether the system is 32-bit.""" + return sys.maxsize <= 2**32 + + @cached_property + def is_64bit(self) -> bool: + """Whether the system is 64-bit.""" + return sys.maxsize > 2**32 + + @cached_property + def is_arm(self) -> bool: + """Whether the system is arm based.""" + return "arm" in platform.machine().lower() + + @cached_property + def is_frozen(self) -> bool: + """Whether or not app is running from a frozen package.""" + if getattr(sys, "frozen", False): + return True + return False + + @cached_property + def is_x86(self) -> bool: + """Whether the system is x86.""" + return platform.machine().lower() in ("i386", "amd64", "x86_64") + + @cached_property + def os(self) -> OsInfo: + """Operating system information.""" + return OsInfo() + + @classmethod + def clear_singleton(cls) -> None: + """Clear singleton instances. + + Intended to only be used for running tests. + + """ + cls.__instance = None diff --git a/f_lib/aws/aws_lambda/type_defs.py b/f_lib/aws/aws_lambda/type_defs.py index a8bca2c..7f7d209 100644 --- a/f_lib/aws/aws_lambda/type_defs.py +++ b/f_lib/aws/aws_lambda/type_defs.py @@ -52,17 +52,6 @@ class LambdaContext: This object provides methods and properties that provide information about the invocation, function, and execution environment. - Attributes: - function_name (str): - invoked_function_arn (str): - memory_limit_in_mb (str): - aws_request_id (str): - log_group_name (str): - log_stream_name (str): - identity (Optional[:class:`LambdaCognitoIdentity`]): - client_context (Optional[:class:`LambdaClientContext`]): - - """ aws_request_id: str @@ -106,23 +95,19 @@ def get_remaining_time_in_millis() -> int: class LambdaSqsEventRecordAttributes(TypedDict): - """Attributes of an SQS message that are set by the SQS service. - - Attributes: - ApproximateFirstReceiveTimestamp (str): The time the message was first - received from the queue (epoch time in milliseconds). - ApproximateReceiveCount (str): The number of times a message has been - received from the queue but not deleted. - SenderId (str): ID for IAM user/role/etc that sent the message. - SentTimestamp (str): The time the message was sent to the queue (epoch - time in milliseconds). - - """ + """Attributes of an SQS message that are set by the SQS service.""" ApproximateFirstReceiveTimestamp: str + """The time the message was first received from the queue (epoch time in milliseconds).""" + ApproximateReceiveCount: str + """The number of times a message has been received from the queue but not deleted.""" + SenderId: str + """ID for IAM user/role/etc that sent the message.""" + SentTimestamp: str + """The time the message was sent to the queue (epoch time in milliseconds).""" class LambdaSqsEventMessageAttributes(TypedDict): @@ -130,60 +115,65 @@ class LambdaSqsEventMessageAttributes(TypedDict): https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-attributes.html - Attributes: - Name (str): The message attribute value. - Type (str): The message attribute data type. Supported types include - ``String``, ``Number``, and ``Binary``. - Value (Union[bytes, float, int, str]): The message attribute value. - """ Name: str + """The message attribute value.""" + Type: str + """The message attribute data type. + + Supported types include ``String``, ``Number``, and ``Binary``. + + """ + Value: bytes | (float | (int | str)) + """The message attribute value.""" class LambdaSqsEventRecord(TypedDict): - """Record from a Lambda invocation event from an SQS Queue. - - Attributes: - attributes (:class:`LambdaSqsEventRecordAttributes`): Attributes of - an SQS message that are set by the SQS service. - awsRegion (str): AWS region code where the Queue is located. - body (str): The message's contents (not URL-encoded). - eventSource (str): AWS service that the event came from. - eventSourceARN (str): ARN of the AWS resource that the event came from. - md5OfBody (str): An MD5 digest of the non-URL-encoded message body - string. - messageId (str): A unique identifier for the message. A messageId is - considered unique across all AWS accounts for an extended - period of time. - messageAttributes (List[:class:`LambdaSqsEventMessageAttributes`]): - Optional metadata that can be added to an SQS message. - receiptHandle (str): An identifier associated with the act of - receiving the message. A new receipt handle is returned every time - you receive a message. When deleting a message, you provide the - last received receipt handle to delete the message. - - """ + """Record from a Lambda invocation event from an SQS Queue.""" attributes: LambdaSqsEventRecordAttributes + """Attributes of an SQS message that are set by the SQS service.""" + awsRegion: str + """AWS region code where the Queue is located.""" + body: str + """The message's contents (not URL-encoded).""" + eventSource: str + """AWS service that the event came from.""" + eventSourceARN: str + """ARN of the AWS resource that the event came from.""" + md5OfBody: str + """An MD5 digest of the non-URL-encoded message body string.""" + messageId: str + """A unique identifier for the message. + + A messageId is considered unique across all AWS accounts for an extended + period of time. + + """ + messageAttributes: list[LambdaSqsEventMessageAttributes] + """Optional metadata that can be added to an SQS message.""" + receiptHandle: str + """An identifier associated with the act of receiving the message. + A new receipt handle is returned every time you receive a message. + When deleting a message, you provide the last received receipt handle to delete the message. -class LambdaSqsEvent(TypedDict): - """Lambda invocation event from an SQS Queue. + """ - Attributes: - Records (List[:class:`LambdaSqsEventRecord`]): List of SQS messages. - """ +class LambdaSqsEvent(TypedDict): + """Lambda invocation event from an SQS Queue.""" Records: list[LambdaSqsEventRecord] + """List of SQS messages.""" diff --git a/f_lib/constants.py b/f_lib/constants.py new file mode 100644 index 0000000..c30320a --- /dev/null +++ b/f_lib/constants.py @@ -0,0 +1,5 @@ +"""Constants.""" +import re + +ANSI_ESCAPE_PATTERN = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") +"""Pattern to match ANSI escape sequences.""" diff --git a/f_lib/mixins/__init__.py b/f_lib/mixins/__init__.py new file mode 100644 index 0000000..9d71d36 --- /dev/null +++ b/f_lib/mixins/__init__.py @@ -0,0 +1,8 @@ +"""Class mixins.""" +from ._cli_interface import CliInterfaceMixin +from ._del_cached_prop import DelCachedPropMixin + +__all__ = [ + "CliInterfaceMixin", + "DelCachedPropMixin", +] diff --git a/f_lib/mixins/_cli_interface.py b/f_lib/mixins/_cli_interface.py new file mode 100644 index 0000000..115afba --- /dev/null +++ b/f_lib/mixins/_cli_interface.py @@ -0,0 +1,191 @@ +"""CLI interface mixin.""" +from __future__ import annotations + +import logging +import shutil +import subprocess +from typing import IO, TYPE_CHECKING, ClassVar, Literal, cast, overload + +from ..constants import ANSI_ESCAPE_PATTERN +from ..utils import convert_kwargs_to_shell_list, convert_list_to_shell_str + +if TYPE_CHECKING: + import pathlib + from collections.abc import Iterable + + from .._environment import Environment + +LOGGER = logging.getLogger(__name__) + + +class CliInterfaceMixin: + """Mixin for adding CLI interface methods.""" + + EXECUTABLE: ClassVar[str] + """CLI executable.""" + + env: Environment + """Environment.""" + + cwd: pathlib.Path + """Working directory where commands will be run.""" + + @classmethod + def found_in_path(cls) -> bool: + """Determine if executable is found in $PATH.""" + if shutil.which(cls.EXECUTABLE): + return True + return False + + @classmethod + def generate_command( + cls, + __command: list[str] | str | None = None, + **kwargs: bool | Iterable[pathlib.Path] | Iterable[str] | str | None, + ) -> list[str]: + """Generate command to be executed and log it. + + Args: + __command: Command to run. + **kwargs: Additional args to pass to the command. + + Returns: + The full command to be passed into a subprocess. + + """ + cmd = [ + cls.EXECUTABLE, + *( + __command + if isinstance(__command, list) + else ([__command] if __command else []) + ), + ] + cmd.extend(convert_kwargs_to_shell_list(**kwargs)) + LOGGER.debug("generated command: %s", convert_list_to_shell_str(cmd)) + return cmd + + @overload + def _run_command( + self, + command: Iterable[str] | str, + *, + capture_output: Literal[True], + env: dict[str, str] | None = ..., + ) -> str: + ... + + @overload + def _run_command( + self, + command: Iterable[str] | str, + *, + capture_output: bool = ..., + env: dict[str, str] | None = ..., + suppress_output: Literal[True] = ..., + ) -> str: + ... + + @overload + def _run_command( + self, + command: Iterable[str] | str, + *, + env: dict[str, str] | None = ..., + suppress_output: Literal[False], + ) -> None: + ... + + @overload + def _run_command( + self, + command: Iterable[str] | str, + *, + capture_output: bool = ..., + env: dict[str, str] | None = ..., + suppress_output: bool = ..., + ) -> str | None: + ... + + def _run_command( + self, + command: Iterable[str] | str, + *, + capture_output: bool = False, + env: dict[str, str] | None = None, + suppress_output: bool = True, + ) -> str | None: + """Run command. + + Args: + command: Command to pass to shell to execute. + capture_output: Whether to capture output. + This can be used when not wanting to suppress output but still needing + to process the contents. The output will be buffered and returned as a + string. If ``suppress_output`` is ``True``, this will be ignored. + env: Environment variables. + suppress_output: If ``True``, the output of the subprocess written + to ``sys.stdout`` and ``sys.stderr`` will be captured and + returned as a string instead of being being written directly. + + """ + cmd_str = ( + command if isinstance(command, str) else convert_list_to_shell_str(command) + ) + LOGGER.debug("running command: %s", cmd_str) + if suppress_output: + return subprocess.check_output( + cmd_str, + cwd=self.cwd, + env=env or self.env.vars, + shell=True, # noqa: S602 + stderr=subprocess.STDOUT, # forward stderr to stdout so it is captured + text=True, + ) + if capture_output: + return self._run_command_capture_output(cmd_str, env=env or self.env.vars) + subprocess.check_call( + cmd_str, + cwd=self.cwd, + env=env or self.env.vars, + shell=True, # noqa: S602 + ) + return None + + def _run_command_capture_output( + self, command: str, *, env: dict[str, str] | None = None + ) -> str: + """Run command and capture output while still allowing it to be printed. + + Intended to be called from ``_run_command``. + + Args: + command: Command to pass to shell to execute. + env: Environment variables. + + """ + output_list: list[str] = [] # accumulate output from the buffer + with subprocess.Popen( + command, + bufsize=1, + cwd=self.cwd, + env=env, + shell=True, # noqa: S602 + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + universal_newlines=True, + ) as proc: + with cast(IO[str], proc.stdout): + for line in cast(IO[str], proc.stdout): + print(line, end="") # noqa: T201 + output_list.append(line) + # strip any ANSI escape sequences from output + output = ANSI_ESCAPE_PATTERN.sub("", "".join(output_list)) + if proc.wait() != 0: + raise subprocess.CalledProcessError( + returncode=proc.returncode, + cmd=command, + output=output, + stderr=output, + ) + return output diff --git a/f_lib/mixins/_del_cached_prop.py b/f_lib/mixins/_del_cached_prop.py new file mode 100644 index 0000000..1502f83 --- /dev/null +++ b/f_lib/mixins/_del_cached_prop.py @@ -0,0 +1,19 @@ +"""Delete cached property mixin.""" +from __future__ import annotations + +from contextlib import suppress + + +class DelCachedPropMixin: + """Mixin to handle safely clearing the value of :func:`functools.cached_property`.""" + + def _del_cached_property(self, *names: str) -> None: + """Delete the cached value of a :func:`functools.cached_property`. + + Args: + names: Names of the attribute that is cached. Can provide one or multiple. + + """ + for name in names: + with suppress(AttributeError): + delattr(self, name) diff --git a/f_lib/py.typed b/f_lib/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/f_lib/utils/__init__.py b/f_lib/utils/__init__.py new file mode 100644 index 0000000..041bd50 --- /dev/null +++ b/f_lib/utils/__init__.py @@ -0,0 +1,43 @@ +"""Utilities.""" +from __future__ import annotations + +import platform +import shlex +import subprocess +from typing import TYPE_CHECKING, Any, cast + +if TYPE_CHECKING: + import pathlib + from collections.abc import Iterable + + +def convert_kwargs_to_shell_list( + **kwargs: bool | Iterable[pathlib.Path] | Iterable[str] | str | None, +) -> list[str]: + """Convert kwargs to a list of shell arguments.""" + result: list[str] = [] + for k, v in kwargs.items(): + if isinstance(v, str): + result.extend([convert_to_cli_flag(k), v]) + elif isinstance(v, list | set | tuple): + for i in cast("Iterable[Any]", v): + result.extend([convert_to_cli_flag(k), str(i)]) + elif isinstance(v, bool) and v: + result.append(convert_to_cli_flag(k)) + return result + + +def convert_list_to_shell_str(split_command: Iterable[str]) -> str: + """Combine a list of strings into a string that can be run as a command. + + Handles multi-platform differences. + + """ + if platform.system() == "Windows": + return subprocess.list2cmdline(split_command) + return shlex.join(split_command) + + +def convert_to_cli_flag(arg_name: str, *, prefix: str = "--") -> str: + """Convert string kwarg name into a CLI flag.""" + return f"{prefix}{arg_name.replace('_', '-')}" diff --git a/package-lock.json b/package-lock.json index bf1ab07..90cf2cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,182 +8,119 @@ "name": "f-lib", "version": "0.0.0", "devDependencies": { - "@itprokyle/cspell-dict": "^1.0.0", - "cspell": "^7.3.6", - "pyright": "^1.1.328" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" + "@itprokyle/cspell-dict": "^1.1.0", + "cspell": "^8.2.3", + "pyright": "^1.1.343" } }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-7.3.6.tgz", - "integrity": "sha512-9T0fFdHbKJXAQgQjLJ9SjtlHvKceKE2Vpa2sdnIXz3K1/coLLF04wHM/wzEPe2VXjYZjbjBatBRfTGjzRGJlbw==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.2.3.tgz", + "integrity": "sha512-AmKr/laSnmuTlECsIkf71N8FPd/ualJx13OdIJNIvUjIE741x/EACITIWLnTK9qFbsefOYp7bUeo9Xtbdw5JSA==", "dev": true, "dependencies": { "@cspell/dict-ada": "^4.0.2", - "@cspell/dict-aws": "^4.0.0", - "@cspell/dict-bash": "^4.1.1", - "@cspell/dict-companies": "^3.0.22", - "@cspell/dict-cpp": "^5.0.5", + "@cspell/dict-aws": "^4.0.1", + "@cspell/dict-bash": "^4.1.3", + "@cspell/dict-companies": "^3.0.28", + "@cspell/dict-cpp": "^5.0.10", "@cspell/dict-cryptocurrencies": "^4.0.0", "@cspell/dict-csharp": "^4.0.2", - "@cspell/dict-css": "^4.0.7", + "@cspell/dict-css": "^4.0.12", "@cspell/dict-dart": "^2.0.3", "@cspell/dict-django": "^4.1.0", "@cspell/dict-docker": "^1.1.7", "@cspell/dict-dotnet": "^5.0.0", "@cspell/dict-elixir": "^4.0.3", - "@cspell/dict-en_us": "^4.3.7", + "@cspell/dict-en_us": "^4.3.12", "@cspell/dict-en-common-misspellings": "^1.0.2", "@cspell/dict-en-gb": "1.1.33", - "@cspell/dict-filetypes": "^3.0.1", + "@cspell/dict-filetypes": "^3.0.3", "@cspell/dict-fonts": "^4.0.0", - "@cspell/dict-fsharp": "^1.0.0", + "@cspell/dict-fsharp": "^1.0.1", "@cspell/dict-fullstack": "^3.1.5", "@cspell/dict-gaming-terms": "^1.0.4", "@cspell/dict-git": "^2.0.0", - "@cspell/dict-golang": "^6.0.2", + "@cspell/dict-golang": "^6.0.5", "@cspell/dict-haskell": "^4.0.1", - "@cspell/dict-html": "^4.0.3", + "@cspell/dict-html": "^4.0.5", "@cspell/dict-html-symbol-entities": "^4.0.0", - "@cspell/dict-java": "^5.0.5", - "@cspell/dict-k8s": "^1.0.1", + "@cspell/dict-java": "^5.0.6", + "@cspell/dict-k8s": "^1.0.2", "@cspell/dict-latex": "^4.0.0", "@cspell/dict-lorem-ipsum": "^4.0.0", - "@cspell/dict-lua": "^4.0.1", + "@cspell/dict-lua": "^4.0.3", + "@cspell/dict-makefile": "^1.0.0", "@cspell/dict-node": "^4.0.3", - "@cspell/dict-npm": "^5.0.8", - "@cspell/dict-php": "^4.0.2", - "@cspell/dict-powershell": "^5.0.2", - "@cspell/dict-public-licenses": "^2.0.3", - "@cspell/dict-python": "^4.1.8", + "@cspell/dict-npm": "^5.0.14", + "@cspell/dict-php": "^4.0.4", + "@cspell/dict-powershell": "^5.0.3", + "@cspell/dict-public-licenses": "^2.0.5", + "@cspell/dict-python": "^4.1.10", "@cspell/dict-r": "^2.0.1", - "@cspell/dict-ruby": "^5.0.0", + "@cspell/dict-ruby": "^5.0.2", "@cspell/dict-rust": "^4.0.1", "@cspell/dict-scala": "^5.0.0", - "@cspell/dict-software-terms": "^3.2.3", - "@cspell/dict-sql": "^2.1.1", + "@cspell/dict-software-terms": "^3.3.14", + "@cspell/dict-sql": "^2.1.3", "@cspell/dict-svelte": "^1.0.2", "@cspell/dict-swift": "^2.0.1", - "@cspell/dict-typescript": "^3.1.1", + "@cspell/dict-typescript": "^3.1.2", "@cspell/dict-vue": "^3.0.0" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@cspell/cspell-json-reporter": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-7.3.6.tgz", - "integrity": "sha512-Op0pSKiImhqXHtQGMVCfx+Fc5tFCGeZwww+fFVQnnPwbU/JkhqbW8ZcYgyPF2KK18lzB8bDOHaltKcePkz13OA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.2.3.tgz", + "integrity": "sha512-603qzkEQZueKauvzCeAMKZqcTBEEJEfs3yBsDKx1jYqyMPuTXnh3vmxkPy0paiJuE625BjzlCuvok225u6x9Qw==", "dev": true, "dependencies": { - "@cspell/cspell-types": "7.3.6" + "@cspell/cspell-types": "8.2.3" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@cspell/cspell-pipe": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-7.3.6.tgz", - "integrity": "sha512-tvNgi31f/p8M108YlDhkC8nqLJBpD1mvVqYNxL+kB/aQtkaw0AHKDsuRhg0rU6xL5MAEnoi3fXgT1HoADhJpbA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.2.3.tgz", + "integrity": "sha512-ga39z+K2ZaSQczaRayNUTrz10z7umEdFiK7AdWOQpGmym5JTtTK0ntnKvKKsdSJ9F5I7TZVxgZH6r4CCEPlEEg==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@cspell/cspell-resolver": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-7.3.6.tgz", - "integrity": "sha512-rFmeqhRFfmlq4oh9tYQIIVZ9aWlP88cU48oCBjvwxjj+GambrD/qobWiW9VYl/CQBPVq4S39cTirf5RXbBHMJA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.2.3.tgz", + "integrity": "sha512-H0855Lg0DxWDcT0FtJyqLvUqOJuE1qSg9X3ENs/ltZntQeaU8wZc+B34bXJrGpJVMuiiqHp4w6rcNN3lsOcshQ==", "dev": true, "dependencies": { - "global-dirs": "^3.0.1" + "global-directory": "^4.0.1" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@cspell/cspell-service-bus": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-7.3.6.tgz", - "integrity": "sha512-jRXII9ceuostAqr/eft9RJR44TMzivuUkufhNZG4657alfhjHQBv/gME4QeFt/jOQqsDi/ifDhw5+r8ew/LsJA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.2.3.tgz", + "integrity": "sha512-hMLEzE2MkFir3kii046RecR1JAAfA6RQhLddjwQTq1c8YCWJ4lQEKUdM5x7nr/UpJtsMj8eYZ7CtbbnxQyn7Zg==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@cspell/cspell-types": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-7.3.6.tgz", - "integrity": "sha512-JnuIMJasZtJpZm0+hzr3emkRJ0PP6QWc9zgd3fx4U8W0lHGZ3Zil5peg67SnjmdTVm4UE63UviAl1y6DyD4kLg==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.2.3.tgz", + "integrity": "sha512-AZIC1n7veQSylp9ZAcVDvIaY+oS/vpzFNJ77rzuhEy/B6X/9jzeI8wg/+vWkmhO59q4iF/ZlswWK3UXfeSnUFg==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@cspell/dict-ada": { @@ -193,27 +130,27 @@ "dev": true }, "node_modules/@cspell/dict-aws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.0.tgz", - "integrity": "sha512-1YkCMWuna/EGIDN/zKkW+j98/55mxigftrSFgsehXhPld+ZMJM5J9UuBA88YfL7+/ETvBdd7mwW6IwWsC+/ltQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.1.tgz", + "integrity": "sha512-NXO+kTPQGqaaJKa4kO92NAXoqS+i99dQzf3/L1BxxWVSBS3/k1f3uhmqIh7Crb/n22W793lOm0D9x952BFga3Q==", "dev": true }, "node_modules/@cspell/dict-bash": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.1.tgz", - "integrity": "sha512-8czAa/Mh96wu2xr0RXQEGMTBUGkTvYn/Pb0o+gqOO1YW+poXGQc3gx0YPqILDryP/KCERrNvkWUJz3iGbvwC2A==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.3.tgz", + "integrity": "sha512-tOdI3QVJDbQSwPjUkOiQFhYcu2eedmX/PtEpVWg0aFps/r6AyjUQINtTgpqMYnYuq8O1QUIQqnpx21aovcgZCw==", "dev": true }, "node_modules/@cspell/dict-companies": { - "version": "3.0.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.0.24.tgz", - "integrity": "sha512-zn9QN99yIvhpGl6fZwt0mvHYcsV2w6XDdK2XWA86A0s9A94U1LCCUsvA4wijUclbZEj9ewsNMlidHcV/D329eQ==", + "version": "3.0.29", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.0.29.tgz", + "integrity": "sha512-F/8XnkqjU7jmSDAcD3LSSX+WxCVUWPssqlO4lzGMIK3MNIUt+d48eSIt3pFAIB/Z9y0ojoLHUtWX9HJ1ZtGrXQ==", "dev": true }, "node_modules/@cspell/dict-cpp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.0.5.tgz", - "integrity": "sha512-ojCpQ4z+sHHLJYfvA3SApqQ1BjO/k3TUdDgqR3sVhFl5qjT9yz1/srBNzqCaBBSz/fiO5A8NKdSA9+IFrUHcig==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.0.10.tgz", + "integrity": "sha512-WCRuDrkFdpmeIR6uXQYKU9loMQKNFS4bUhtHdv5fu4qVyJSh3k/kgmtTm1h1BDTj8EwPRc/RGxS+9Z3b2mnabA==", "dev": true }, "node_modules/@cspell/dict-cryptocurrencies": { @@ -229,9 +166,9 @@ "dev": true }, "node_modules/@cspell/dict-css": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.9.tgz", - "integrity": "sha512-uiwdqbyrqynVDl9COs9gJSmIcm76je2yHs6rnI5USJ6y0PXfiBiFKQ7/q8oi2ff9AK8RedsGU4luSor6nLYpVA==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.12.tgz", + "integrity": "sha512-vGBgPM92MkHQF5/2jsWcnaahOZ+C6OE/fPvd5ScBP72oFY9tn5GLuomcyO0z8vWCr2e0nUSX1OGimPtcQAlvSw==", "dev": true }, "node_modules/@cspell/dict-dart": { @@ -271,9 +208,9 @@ "dev": true }, "node_modules/@cspell/dict-en_us": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.8.tgz", - "integrity": "sha512-rCPsbDHuRnFUbzWAY6O1H9+cLZt5FNQwjPVw2TdQZfipdb0lim984aLGY+nupi1iKC3lfjyd5SVUgmSZEG1QNA==", + "version": "4.3.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.12.tgz", + "integrity": "sha512-1bsUxFjgxF30FTzcU5uvmCvH3lyqVKR9dbwsJhomBlUM97f0edrd6590SiYBXDm7ruE68m3lJd4vs0Ev2D6FtQ==", "dev": true }, "node_modules/@cspell/dict-en-common-misspellings": { @@ -289,9 +226,9 @@ "dev": true }, "node_modules/@cspell/dict-filetypes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.1.tgz", - "integrity": "sha512-8z8mY1IbrTyTRumx2vvD9yzRhNMk9SajM/GtI5hdMM2pPpNSp25bnuauzjRf300eqlqPY2MNb5MmhBFO014DJw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.3.tgz", + "integrity": "sha512-J9UP+qwwBLfOQ8Qg9tAsKtSY/WWmjj21uj6zXTI9hRLD1eG1uUOLcfVovAmtmVqUWziPSKMr87F6SXI3xmJXgw==", "dev": true }, "node_modules/@cspell/dict-fonts": { @@ -301,9 +238,9 @@ "dev": true }, "node_modules/@cspell/dict-fsharp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.0.0.tgz", - "integrity": "sha512-dHPkMHwW4dWv3Lv9VWxHuVm4IylqvcfRBSnZ7usJTRThraetSVrOPIJwr6UJh7F5un/lGJx2lxWVApf2WQaB/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.0.1.tgz", + "integrity": "sha512-23xyPcD+j+NnqOjRHgW3IU7Li912SX9wmeefcY0QxukbAxJ/vAN4rBpjSwwYZeQPAn3fxdfdNZs03fg+UM+4yQ==", "dev": true }, "node_modules/@cspell/dict-fullstack": { @@ -325,9 +262,9 @@ "dev": true }, "node_modules/@cspell/dict-golang": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.2.tgz", - "integrity": "sha512-5pyZn4AAiYukAW+gVMIMVmUSkIERFrDX2vtPDjg8PLQUhAHWiVeQSDjuOhq9/C5GCCEZU/zWSONkGiwLBBvV9A==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.5.tgz", + "integrity": "sha512-w4mEqGz4/wV+BBljLxduFNkMrd3rstBNDXmoX5kD4UTzIb4Sy0QybWCtg2iVT+R0KWiRRA56QKOvBsgXiddksA==", "dev": true }, "node_modules/@cspell/dict-haskell": { @@ -337,9 +274,9 @@ "dev": true }, "node_modules/@cspell/dict-html": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.4.tgz", - "integrity": "sha512-CWFe9jt1g7asuRMGUguqz8+53BJjDnkafayavXk2+f/KGQ7mwyQtVAjf/gD9h1w7qO+NwXIbYweFkbQ8ki6+gQ==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.5.tgz", + "integrity": "sha512-p0brEnRybzSSWi8sGbuVEf7jSTDmXPx7XhQUb5bgG6b54uj+Z0Qf0V2n8b/LWwIPJNd1GygaO9l8k3HTCy1h4w==", "dev": true }, "node_modules/@cspell/dict-html-symbol-entities": { @@ -355,9 +292,9 @@ "dev": true }, "node_modules/@cspell/dict-k8s": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.1.tgz", - "integrity": "sha512-gc5y4Nm3hVdMZNBZfU2M1AsAmObZsRWjCUk01NFPfGhFBXyVne41T7E62rpnzu5330FV/6b/TnFcPgRmak9lLw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.2.tgz", + "integrity": "sha512-tLT7gZpNPnGa+IIFvK9SP1LrSpPpJ94a/DulzAPOb1Q2UBFwdpFd82UWhio0RNShduvKG/WiMZf/wGl98pn+VQ==", "dev": true }, "node_modules/@cspell/dict-latex": { @@ -373,9 +310,15 @@ "dev": true }, "node_modules/@cspell/dict-lua": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.1.tgz", - "integrity": "sha512-j0MFmeCouSoC6EdZTbvGe1sJ9V+ruwKSeF+zRkNNNload7R72Co5kX1haW2xLHGdlq0kqSy1ODRZKdVl0e+7hg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.3.tgz", + "integrity": "sha512-lDHKjsrrbqPaea13+G9s0rtXjMO06gPXPYRjRYawbNmo4E/e3XFfVzeci3OQDQNDmf2cPOwt9Ef5lu2lDmwfJg==", + "dev": true + }, + "node_modules/@cspell/dict-makefile": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.0.tgz", + "integrity": "sha512-3W9tHPcSbJa6s0bcqWo6VisEDTSN5zOtDbnPabF7rbyjRpNo0uHXHRJQF8gAbFzoTzBBhgkTmrfSiuyQm7vBUQ==", "dev": true }, "node_modules/@cspell/dict-node": { @@ -385,33 +328,33 @@ "dev": true }, "node_modules/@cspell/dict-npm": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.0.9.tgz", - "integrity": "sha512-+MqhnE+QI3M1OKV8QsM8vKRHsrvN84G/I0NClloEXTovUexCit8UwcHdlWK7dTbtmYUvEJglCTUG5DWqxwOlhw==", + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.0.14.tgz", + "integrity": "sha512-k0kC7/W2qG5YII+SW6s+JtvKrkZg651vizi5dv/5G2HmJaeLNgDqBVeeDk/uV+ntBorM66XG4BPMjSxoaIlC5w==", "dev": true }, "node_modules/@cspell/dict-php": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.3.tgz", - "integrity": "sha512-PxtSmWJCDEB4M8R9ER9ijxBum/tvUqYT26QeuV58q2IFs5IrPZ6hocQKvnFGXItjCWH4oYXyAEAAzINlBC4Opg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.5.tgz", + "integrity": "sha512-9r8ao7Z/mH9Z8pSB7yLtyvcCJWw+/MnQpj7xGVYzIV7V2ZWDRjXZAMgteHMJ37m8oYz64q5d4tiipD300QSetQ==", "dev": true }, "node_modules/@cspell/dict-powershell": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.2.tgz", - "integrity": "sha512-IHfWLme3FXE7vnOmMncSBxOsMTdNWd1Vcyhag03WS8oANSgX8IZ+4lMI00mF0ptlgchf16/OU8WsV4pZfikEFw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.3.tgz", + "integrity": "sha512-lEdzrcyau6mgzu1ie98GjOEegwVHvoaWtzQnm1ie4DyZgMr+N6D0Iyj1lzvtmt0snvsDFa5F2bsYzf3IMKcpcA==", "dev": true }, "node_modules/@cspell/dict-public-licenses": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.4.tgz", - "integrity": "sha512-KjsfuGwMWvPkp6s0nR+s4mZc9SQhh1tHDOyQZfEVRwi+2ev7f8l7R6ts9sP2Mplb8UcxwO6YmKwxHjN+XHoMoA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.5.tgz", + "integrity": "sha512-91HK4dSRri/HqzAypHgduRMarJAleOX5NugoI8SjDLPzWYkwZ1ftuCXSk+fy8DLc3wK7iOaFcZAvbjmnLhVs4A==", "dev": true }, "node_modules/@cspell/dict-python": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.1.8.tgz", - "integrity": "sha512-yFrO9gGI3KIbw0Y1odAEtagrzmthjJVank9B7qlsSQvN78RgD1JQQycTadNWpzdjCj+JuiiH8pJBFWflweZoxw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.1.11.tgz", + "integrity": "sha512-XG+v3PumfzUW38huSbfT15Vqt3ihNb462ulfXifpQllPok5OWynhszCLCRQjQReV+dgz784ST4ggRxW452/kVg==", "dev": true, "dependencies": { "@cspell/dict-data-science": "^1.0.11" @@ -424,9 +367,9 @@ "dev": true }, "node_modules/@cspell/dict-ruby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.0.tgz", - "integrity": "sha512-ssb96QxLZ76yPqFrikWxItnCbUKhYXJ2owkoIYzUGNFl2CHSoHCb5a6Zetum9mQ/oUA3gNeUhd28ZUlXs0la2A==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.2.tgz", + "integrity": "sha512-cIh8KTjpldzFzKGgrqUX4bFyav5lC52hXDKo4LbRuMVncs3zg4hcSf4HtURY+f2AfEZzN6ZKzXafQpThq3dl2g==", "dev": true }, "node_modules/@cspell/dict-rust": { @@ -442,15 +385,15 @@ "dev": true }, "node_modules/@cspell/dict-software-terms": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-3.3.1.tgz", - "integrity": "sha512-nZtlPNe3se9Maj6HQhABUAG9HzgKvAmwli0WoITlxxhlfU4on74evZJ7FtJpUTCXSkAXgKWz8pMQtsRXvRY40w==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-3.3.15.tgz", + "integrity": "sha512-1qqMGFi1TUNq9gQj4FTLPTlqVzQLXrj80MsKoXVpysr+823kMWesQAjqHiPg+MYsQ3DlTcpGWcjq/EbYonqueQ==", "dev": true }, "node_modules/@cspell/dict-sql": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.1.1.tgz", - "integrity": "sha512-v1mswi9NF40+UDUMuI148YQPEQvWjac72P6ZsjlRdLjEiQEEMEsTQ+zlkIdnzC9QCNyJaqD5Liq9Mn78/8Zxtw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.1.3.tgz", + "integrity": "sha512-SEyTNKJrjqD6PAzZ9WpdSu6P7wgdNtGV2RV8Kpuw1x6bV+YsSptuClYG+JSdRExBTE6LwIe1bTklejUp3ZP8TQ==", "dev": true }, "node_modules/@cspell/dict-svelte": { @@ -478,33 +421,50 @@ "dev": true }, "node_modules/@cspell/dynamic-import": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-7.3.6.tgz", - "integrity": "sha512-NLWawhLkfTSkf36UwYJrRyMh3snXOHhuRFO7eVanPqE7oeU+1+OF/C467sYdiJGZnrCL3ojIr399JTVMz148Iw==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.2.3.tgz", + "integrity": "sha512-udJF+88F4UMH2eVKe3Utsh4X1PyNwqPJclIeD3/MDMFWm16lLkFYMqqrdr51tNLKVi4cXceGrUEapmGwf87l/w==", "dev": true, "dependencies": { - "import-meta-resolve": "^3.0.0" + "import-meta-resolve": "^4.0.0" }, "engines": { - "node": ">=16" + "node": ">=18.0" } }, "node_modules/@cspell/strong-weak-map": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-7.3.6.tgz", - "integrity": "sha512-PoVFTvY8CGhc+7W3uvyPUWIBakc+ga9X5QpSkFI/HQghmaGDDaaQBfbuv/LsS7T9bkEoWz4jLtJoNBas870gZA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.2.3.tgz", + "integrity": "sha512-/0gQZw87MqGX8f28E+LhFfrsWdRdQEL8EEQeMXrrzSoPnfSz+ItHMhhrwPF+bMePPjaaUNYoRXvX7hxiDsGm0w==", "dev": true, "engines": { - "node": ">=16" + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/@itprokyle/cspell-dict": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@itprokyle/cspell-dict/-/cspell-dict-1.0.0.tgz", - "integrity": "sha512-tdN+YXU0oHv59Z+u3rLMKvsNJsWJbFtC8RyB10RTgOX5ZGF5cCR35bfhNMRqlw8SXD8UdChKrg1Ro1dlZ1ulRA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@itprokyle/cspell-dict/-/cspell-dict-1.1.0.tgz", + "integrity": "sha512-ITWhjhAuN66sbsnAIEmASeYJSTwWy19Ce2aNi8Jjmut4MJwPki73UadIWM2pt2O+Zmb0OxsjIJTUFWOZuix87w==", "dev": true, "peerDependencies": { - "cspell": "^7.0.0" + "cspell": "^7.0.0 || ^8.0.0" } }, "node_modules/@nodelib/fs.scandir": { @@ -542,6 +502,16 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -555,23 +525,17 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/array-timsort": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", @@ -585,13 +549,12 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -659,24 +622,27 @@ } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "engines": { "node": ">=16" @@ -698,12 +664,6 @@ "node": ">= 6" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, "node_modules/configstore": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", @@ -729,19 +689,18 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, - "node_modules/cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=14" + "node": ">= 8" } }, "node_modules/crypto-random-string": { @@ -760,247 +719,171 @@ } }, "node_modules/cspell": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-7.3.6.tgz", - "integrity": "sha512-iN3D05nwCbS6MdignKwK97vQPX3yrT/Nsu3LhhFptU0O5PO4hvRzFuSzEq+AumMby4Tuf9HcGP5Ugvyi7Gb3gw==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-8.2.3.tgz", + "integrity": "sha512-lJEIglmBINLW4Jwn+5W1k6Zb5EjyRFLnTvc1uQ268/9pcsB+GWUZruplIe5+erR3AxZ+N7Tqp7IY9j2Jf1+/Fg==", "dev": true, "dependencies": { - "@cspell/cspell-json-reporter": "7.3.6", - "@cspell/cspell-pipe": "7.3.6", - "@cspell/cspell-types": "7.3.6", - "@cspell/dynamic-import": "7.3.6", + "@cspell/cspell-json-reporter": "8.2.3", + "@cspell/cspell-pipe": "8.2.3", + "@cspell/cspell-types": "8.2.3", + "@cspell/dynamic-import": "8.2.3", "chalk": "^5.3.0", "chalk-template": "^1.1.0", - "commander": "^11.0.0", - "cspell-gitignore": "7.3.6", - "cspell-glob": "7.3.6", - "cspell-io": "7.3.6", - "cspell-lib": "7.3.6", - "fast-glob": "^3.3.1", + "commander": "^11.1.0", + "cspell-gitignore": "8.2.3", + "cspell-glob": "8.2.3", + "cspell-io": "8.2.3", + "cspell-lib": "8.2.3", + "fast-glob": "^3.3.2", "fast-json-stable-stringify": "^2.1.0", - "file-entry-cache": "^7.0.0", + "file-entry-cache": "^8.0.0", "get-stdin": "^9.0.0", "semver": "^7.5.4", "strip-ansi": "^7.1.0", - "vscode-uri": "^3.0.7" + "vscode-uri": "^3.0.8" }, "bin": { "cspell": "bin.mjs", "cspell-esm": "bin.mjs" }, "engines": { - "node": ">=16" + "node": ">=18" }, "funding": { "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" } }, + "node_modules/cspell-config-lib": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.2.3.tgz", + "integrity": "sha512-ATbOR06GKBIFM5SPKMF4fgo5G2qmOfdV8TbpyzNtw1AGL7PoOgDNFiKSutEzO5EHyZuXE71ZFxH3rVr2gIV7Dw==", + "dev": true, + "dependencies": { + "@cspell/cspell-types": "8.2.3", + "comment-json": "^4.2.3", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/cspell-dictionary": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-7.3.6.tgz", - "integrity": "sha512-8E0qsGTP7uHZeQ0qD6au+bjaj4M9F4AgurssG3VQuvsYpzEI6S/81U3GQVzcn/4mn7Z5KE286CElZQWAiQPLQA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.2.3.tgz", + "integrity": "sha512-M/idc3TLjYMpT4+8PlIg7kzoeGkR7o6h6pTwRfy/ZkBkEaV+U/35ZtVLO4qjxnuX6wrmawYmHhYqgzyKLEJIhw==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "7.3.6", - "@cspell/cspell-types": "7.3.6", - "cspell-trie-lib": "7.3.6", - "fast-equals": "^4.0.3", + "@cspell/cspell-pipe": "8.2.3", + "@cspell/cspell-types": "8.2.3", + "cspell-trie-lib": "8.2.3", + "fast-equals": "^5.0.1", "gensequence": "^6.0.0" }, "engines": { - "node": ">=16" + "node": ">=18" } }, - "node_modules/cspell-dictionary/node_modules/fast-equals": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", - "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==", - "dev": true - }, "node_modules/cspell-gitignore": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-7.3.6.tgz", - "integrity": "sha512-D/oWUoeW3kgKIIpLpJCJk4KmtxPdb6yqkMX8Ze4rzMXAUjHkw6PPjMd8hcJl7uTJa4T8vHM+UR6L4t3huDuVoA==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.2.3.tgz", + "integrity": "sha512-tPUI+Aoq1b1shD04CLprrS8wEriiF4G1J+qBiCZK2KWOh6IcufuuDhP1Jtkzz9uONgGWFPF6jj/9TXRFlQexbQ==", "dev": true, "dependencies": { - "cspell-glob": "7.3.6", - "find-up": "^5.0.0" + "cspell-glob": "8.2.3", + "find-up-simple": "^1.0.0" }, "bin": { "cspell-gitignore": "bin.mjs" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/cspell-glob": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-7.3.6.tgz", - "integrity": "sha512-xfVmqkkg/Pznij3VJCLbUvEKWqs/+AyyHIXo9s1j/d4M0Nw/O4HJFoHwNiMoAk6aceMTgjjVIneGmSZsHVGYZg==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.2.3.tgz", + "integrity": "sha512-byP2kBblO5d9rZr73MPor+KfoFdry4uu/MQmwLiK5mxgmokZYv5GVDX2DrO16Ni4yJ6/2rBPWLfq+DfCXSWqyw==", "dev": true, "dependencies": { "micromatch": "^4.0.5" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/cspell-grammar": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-7.3.6.tgz", - "integrity": "sha512-04kvcptwvJBSMfcOTbanEFa194Xkpkjo4wkTImO26Zzu06tGawbL4FPPQdGygMz7yTdc6Wlrlks5TNChWlcn+Q==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.2.3.tgz", + "integrity": "sha512-z57Qyu24BsHHp/nZ9ftN377cSCgSJg+6oywIglau7ws7vRpUgYKVoKxn+ZJfOrIZpXfZUqgph5IwAGFI+aRN6w==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "7.3.6", - "@cspell/cspell-types": "7.3.6" + "@cspell/cspell-pipe": "8.2.3", + "@cspell/cspell-types": "8.2.3" }, "bin": { "cspell-grammar": "bin.mjs" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/cspell-io": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-7.3.6.tgz", - "integrity": "sha512-FzynVc3OE9rS4t0cxTCVD9VFwOAnhvhV/WBWMrMUtvi8DVnRu7of/1ZJsC+XDtij+G1Kd6EOrzSnTj5gn9aQaQ==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-8.2.3.tgz", + "integrity": "sha512-mPbLXiIje9chncy/Xb9C6AxqjJm9AFHz/nmIIP5bc6gd4w/yaGlQNyO8jjHF1u2JBVbIxPQSMjFgEuqasPy4Sg==", "dev": true, "dependencies": { - "@cspell/cspell-service-bus": "7.3.6", - "node-fetch": "^2.7.0" + "@cspell/cspell-service-bus": "8.2.3" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/cspell-lib": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-7.3.6.tgz", - "integrity": "sha512-ixPnudlaNh4UwFkHeKUXbBYB/wLHNv1Gf+zBGy4oz2Uu9ZZTVgczhE/t2pPTD6ZRcq4+YulGuqxYCS+3qqOQQQ==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.2.3.tgz", + "integrity": "sha512-NA4FsGomGPNp15TWbXx13bfknLGU8B66j0QlU3i4oDrWBj/t5m7O1nmiQqcaDSKd9s5HtdTHfxLc83hdzmmizg==", "dev": true, "dependencies": { - "@cspell/cspell-bundled-dicts": "7.3.6", - "@cspell/cspell-pipe": "7.3.6", - "@cspell/cspell-resolver": "7.3.6", - "@cspell/cspell-types": "7.3.6", - "@cspell/dynamic-import": "7.3.6", - "@cspell/strong-weak-map": "7.3.6", + "@cspell/cspell-bundled-dicts": "8.2.3", + "@cspell/cspell-pipe": "8.2.3", + "@cspell/cspell-resolver": "8.2.3", + "@cspell/cspell-types": "8.2.3", + "@cspell/dynamic-import": "8.2.3", + "@cspell/strong-weak-map": "8.2.3", "clear-module": "^4.1.2", "comment-json": "^4.2.3", "configstore": "^6.0.0", - "cosmiconfig": "8.0.0", - "cspell-dictionary": "7.3.6", - "cspell-glob": "7.3.6", - "cspell-grammar": "7.3.6", - "cspell-io": "7.3.6", - "cspell-trie-lib": "7.3.6", + "cspell-config-lib": "8.2.3", + "cspell-dictionary": "8.2.3", + "cspell-glob": "8.2.3", + "cspell-grammar": "8.2.3", + "cspell-io": "8.2.3", + "cspell-trie-lib": "8.2.3", "fast-equals": "^5.0.1", - "find-up": "^6.3.0", "gensequence": "^6.0.0", "import-fresh": "^3.3.0", "resolve-from": "^5.0.0", - "vscode-languageserver-textdocument": "^1.0.8", - "vscode-uri": "^3.0.7" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/cspell-lib/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cspell-lib/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cspell-lib/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cspell-lib/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cspell-lib/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/cspell-lib/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, "node_modules/cspell-trie-lib": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-7.3.6.tgz", - "integrity": "sha512-75lSsKTdmFpewEl8Q+/WnSbpZ+JjoNnSDobNDcjZHTTnj/TlgCVxXASTaFLlXnqWU51QX+5798smnqpWBcJigg==", + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.2.3.tgz", + "integrity": "sha512-yN2PwceN9ViCjXUhhi3MTWfi15Rpc9CsSFFPV3A6cOWoB0qBnuTXk8hBSx+427UGYjtlXPP6EZKY8w8OK6PweA==", "dev": true, "dependencies": { - "@cspell/cspell-pipe": "7.3.6", - "@cspell/cspell-types": "7.3.6", + "@cspell/cspell-pipe": "8.2.3", + "@cspell/cspell-types": "8.2.3", "gensequence": "^6.0.0" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/dot-prop": { @@ -1018,23 +901,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/esprima": { "version": "4.0.1", @@ -1059,9 +936,9 @@ } }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -1081,24 +958,24 @@ "dev": true }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dev": true, "dependencies": { "reusify": "^1.0.4" } }, "node_modules/file-entry-cache": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-7.0.0.tgz", - "integrity": "sha512-OWhoO9dvvwspdI7YjGrs5wD7bPggVHc5b1NFAdyd1fEPIeno3Fj70fjBhklAqzUefgX7KCNDBnvrT8rZhS8Shw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.1.0" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -1113,34 +990,30 @@ "node": ">=8" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.0.tgz", + "integrity": "sha512-EryKbCE/wxpxKniQlyas6PY1I9vwtF3uCBweX+N8KYTCn3Y12RTGtQAJ/bd5pl7kxUAc8v/R3Ake/N17OZiFqA==", "dev": true, "dependencies": { - "flatted": "^3.2.7", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4", + "rimraf": "^5.0.5" }, "engines": { - "node": ">=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -1149,11 +1022,21 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/fsevents": { "version": "2.3.3", @@ -1191,20 +1074,22 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1222,16 +1107,16 @@ "node": ">= 6" } }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", "dev": true, "dependencies": { - "ini": "2.0.0" + "ini": "4.1.1" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1243,15 +1128,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/has-own-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", @@ -1299,9 +1175,9 @@ } }, "node_modules/import-meta-resolve": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.0.0.tgz", - "integrity": "sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", "dev": true, "funding": { "type": "github", @@ -1317,37 +1193,15 @@ "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1357,6 +1211,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1393,22 +1256,28 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "@isaacs/cliui": "^8.0.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/json-buffer": { @@ -1417,52 +1286,22 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "dependencies": { "json-buffer": "3.0.1" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": "14 || >=16.14" } }, "node_modules/merge2": { @@ -1488,74 +1327,27 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { - "whatwg-url": "^5.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16 || 14 >=14.17" } }, "node_modules/parent-module": { @@ -1570,49 +1362,29 @@ "node": ">=8" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/picomatch": { @@ -1628,9 +1400,9 @@ } }, "node_modules/pyright": { - "version": "1.1.328", - "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.328.tgz", - "integrity": "sha512-LiFIELh/6wVZuvgH+OGZ81ln0EpB8si2gt1M229qKnG4lbh93A0gyXLwu62XtTie8FDUcznmdCEiMal8jxJ7+w==", + "version": "1.1.343", + "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.343.tgz", + "integrity": "sha512-lOsiufTR94E0Z3O7n19q5Zr9maSI0uDEtyke4ACFuA8gwVdcj3ewOUdzCdPchzJuXTAJq2B+qvyGiNOFF6d0vw==", "dev": true, "bin": { "pyright": "index.js", @@ -1692,15 +1464,18 @@ } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "glob": "^10.3.7" }, "bin": { - "rimraf": "bin.js" + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1744,12 +1519,110 @@ "node": ">=10" } }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -1765,16 +1638,26 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "has-flag": "^3.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" } }, "node_modules/to-regex-range": { @@ -1789,12 +1672,6 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, "node_modules/type-fest": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", @@ -1838,33 +1715,117 @@ "dev": true }, "node_modules/vscode-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz", - "integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", "dev": true }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", @@ -1877,6 +1838,12 @@ "typedarray-to-buffer": "^3.1.5" } }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, "node_modules/xdg-basedir": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", @@ -1895,16 +1862,13 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 14" } } } diff --git a/package.json b/package.json index 34008a9..f2a7be3 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,8 @@ { - "author": "Kyle Finley ", "devDependencies": { - "@itprokyle/cspell-dict": "^1.0.0", - "cspell": "^7.3.6", - "pyright": "^1.1.328" + "@itprokyle/cspell-dict": "^1.1.0", + "cspell": "^8.2.3", + "pyright": "^1.1.343" }, "name": "f-lib", "version": "0.0.0" diff --git a/poetry.lock b/poetry.lock index a27dd42..c204e77 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -22,35 +22,53 @@ files = [ {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, ] +[[package]] +name = "beautifulsoup4" +version = "4.12.2" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, + {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + [[package]] name = "black" -version = "23.9.1" +version = "23.12.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"}, - {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"}, - {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"}, - {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"}, - {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"}, - {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"}, - {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"}, - {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"}, - {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"}, - {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"}, - {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"}, - {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"}, - {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"}, - {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"}, - {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"}, - {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"}, - {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"}, - {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"}, - {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"}, - {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"}, - {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"}, - {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"}, + {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, + {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, + {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, + {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, + {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, + {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, + {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, + {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, + {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, + {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, + {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, + {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, + {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, + {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, + {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, + {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, + {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, + {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, + {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, + {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, + {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, + {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, ] [package.dependencies] @@ -59,12 +77,10 @@ mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -260,9 +276,6 @@ files = [ {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli"] @@ -277,30 +290,47 @@ files = [ {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, ] +[[package]] +name = "doc8" +version = "1.1.1" +description = "Style checker for Sphinx (or other) RST documentation" +optional = false +python-versions = ">=3.8" +files = [ + {file = "doc8-1.1.1-py3-none-any.whl", hash = "sha256:e493aa3f36820197c49f407583521bb76a0fde4fffbcd0e092be946ff95931ac"}, + {file = "doc8-1.1.1.tar.gz", hash = "sha256:d97a93e8f5a2efc4713a0804657dedad83745cca4cd1d88de9186f77f9776004"}, +] + +[package.dependencies] +docutils = ">=0.19,<0.21" +Pygments = "*" +restructuredtext-lint = ">=0.7" +stevedore = "*" + [[package]] name = "docutils" -version = "0.18.1" +version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" files = [ - {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"}, - {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"}, + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] -name = "exceptiongroup" -version = "1.1.3" -description = "Backport of PEP 654 (exception groups)" +name = "execnet" +version = "2.0.2" +description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, + {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"}, + {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"}, ] [package.extras] -test = ["pytest (>=6)"] +testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "filelock" @@ -318,6 +348,23 @@ docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] typing = ["typing-extensions (>=4.7.1)"] +[[package]] +name = "furo" +version = "2023.9.10" +description = "A clean customisable Sphinx documentation theme." +optional = false +python-versions = ">=3.8" +files = [ + {file = "furo-2023.9.10-py3-none-any.whl", hash = "sha256:513092538537dc5c596691da06e3c370714ec99bc438680edc1debffb73e5bfc"}, + {file = "furo-2023.9.10.tar.gz", hash = "sha256:5707530a476d2a63b8cad83b4f961f3739a69f4b058bcf38a03a39fa537195b2"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7" +sphinx = ">=6.0,<8.0" +sphinx-basic-ng = "*" + [[package]] name = "identify" version = "2.5.29" @@ -354,25 +401,6 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -[[package]] -name = "importlib-metadata" -version = "6.8.0" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, - {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, -] - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] - [[package]] name = "iniconfig" version = "2.0.0" @@ -545,13 +573,13 @@ files = [ [[package]] name = "platformdirs" -version = "3.10.0" +version = "4.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, - {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, + {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, + {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, ] [package.extras] @@ -575,13 +603,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.4.0" +version = "3.6.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pre_commit-3.4.0-py2.py3-none-any.whl", hash = "sha256:96d529a951f8b677f730a7212442027e8ba53f9b04d217c4c67dc56c393ad945"}, - {file = "pre_commit-3.4.0.tar.gz", hash = "sha256:6bbd5129a64cad4c0dfaeeb12cd8f7ea7e15b77028d985341478c8af3c759522"}, + {file = "pre_commit-3.6.0-py2.py3-none-any.whl", hash = "sha256:c255039ef399049a5544b6ce13d135caba8f2c28c3b4033277a788f434308376"}, + {file = "pre_commit-3.6.0.tar.gz", hash = "sha256:d30bad9abf165f7785c15a21a1f46da7d0677cb00ee7ff4c579fd38922efe15d"}, ] [package.dependencies] @@ -607,22 +635,20 @@ plugins = ["importlib-metadata"] [[package]] name = "pytest" -version = "7.4.2" +version = "7.4.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, + {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, + {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -647,13 +673,13 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-mock" -version = "3.11.1" +version = "3.12.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.11.1.tar.gz", hash = "sha256:7f6b125602ac6d743e523ae0bfa71e1a697a2f5534064528c6ff84c2f7c2fc7f"}, - {file = "pytest_mock-3.11.1-py3-none-any.whl", hash = "sha256:21c279fff83d70763b05f8874cc9cfb3fcacd6d354247a976f9529d19f9acf39"}, + {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, + {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, ] [package.dependencies] @@ -662,6 +688,25 @@ pytest = ">=5.0" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] +[[package]] +name = "pytest-subprocess" +version = "1.5.0" +description = "A plugin to fake subprocess for pytest" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pytest-subprocess-1.5.0.tar.gz", hash = "sha256:d7693b96f588f39b84c7b2b5c04287459246dfae6be1dd4098937a728ad4fbe3"}, + {file = "pytest_subprocess-1.5.0-py3-none-any.whl", hash = "sha256:dfd75b10af6800a89a9b758f2e2eceff9de082a27bd1388521271b6e8bde298b"}, +] + +[package.dependencies] +pytest = ">=4.0.0" + +[package.extras] +dev = ["changelogd", "nox"] +docs = ["changelogd", "furo", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-napoleon"] +test = ["Pygments (>=2.0)", "anyio", "coverage", "docutils (>=0.12)", "pytest (>=4.0)", "pytest-asyncio (>=0.15.1)", "pytest-rerunfailures"] + [[package]] name = "pytest-sugar" version = "0.9.7" @@ -681,6 +726,26 @@ termcolor = ">=2.1.0" [package.extras] dev = ["black", "flake8", "pre-commit"] +[[package]] +name = "pytest-xdist" +version = "3.3.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"}, + {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"}, +] + +[package.dependencies] +execnet = ">=1.1" +pytest = ">=6.2.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + [[package]] name = "pyyaml" version = "6.0.1" @@ -761,30 +826,43 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "restructuredtext-lint" +version = "1.4.0" +description = "reStructuredText linter" +optional = false +python-versions = "*" +files = [ + {file = "restructuredtext_lint-1.4.0.tar.gz", hash = "sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45"}, +] + +[package.dependencies] +docutils = ">=0.11,<1.0" + [[package]] name = "ruff" -version = "0.0.291" -description = "An extremely fast Python linter, written in Rust." +version = "0.1.9" +description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.0.291-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:b97d0d7c136a85badbc7fd8397fdbb336e9409b01c07027622f28dcd7db366f2"}, - {file = "ruff-0.0.291-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6ab44ea607967171e18aa5c80335237be12f3a1523375fa0cede83c5cf77feb4"}, - {file = "ruff-0.0.291-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a04b384f2d36f00d5fb55313d52a7d66236531195ef08157a09c4728090f2ef0"}, - {file = "ruff-0.0.291-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b727c219b43f903875b7503a76c86237a00d1a39579bb3e21ce027eec9534051"}, - {file = "ruff-0.0.291-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87671e33175ae949702774071b35ed4937da06f11851af75cd087e1b5a488ac4"}, - {file = "ruff-0.0.291-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b75f5801547f79b7541d72a211949754c21dc0705c70eddf7f21c88a64de8b97"}, - {file = "ruff-0.0.291-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b09b94efdcd162fe32b472b2dd5bf1c969fcc15b8ff52f478b048f41d4590e09"}, - {file = "ruff-0.0.291-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d5b56bc3a2f83a7a1d7f4447c54d8d3db52021f726fdd55d549ca87bca5d747"}, - {file = "ruff-0.0.291-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13f0d88e5f367b2dc8c7d90a8afdcfff9dd7d174e324fd3ed8e0b5cb5dc9b7f6"}, - {file = "ruff-0.0.291-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b3eeee1b1a45a247758ecdc3ab26c307336d157aafc61edb98b825cadb153df3"}, - {file = "ruff-0.0.291-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6c06006350c3bb689765d71f810128c9cdf4a1121fd01afc655c87bab4fb4f83"}, - {file = "ruff-0.0.291-py3-none-musllinux_1_2_i686.whl", hash = "sha256:fd17220611047de247b635596e3174f3d7f2becf63bd56301fc758778df9b629"}, - {file = "ruff-0.0.291-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5383ba67ad360caf6060d09012f1fb2ab8bd605ab766d10ca4427a28ab106e0b"}, - {file = "ruff-0.0.291-py3-none-win32.whl", hash = "sha256:1d5f0616ae4cdc7a938b493b6a1a71c8a47d0300c0d65f6e41c281c2f7490ad3"}, - {file = "ruff-0.0.291-py3-none-win_amd64.whl", hash = "sha256:8a69bfbde72db8ca1c43ee3570f59daad155196c3fbe357047cd9b77de65f15b"}, - {file = "ruff-0.0.291-py3-none-win_arm64.whl", hash = "sha256:d867384a4615b7f30b223a849b52104214442b5ba79b473d7edd18da3cde22d6"}, - {file = "ruff-0.0.291.tar.gz", hash = "sha256:c61109661dde9db73469d14a82b42a88c7164f731e6a3b0042e71394c1c7ceed"}, + {file = "ruff-0.1.9-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e6a212f436122ac73df851f0cf006e0c6612fe6f9c864ed17ebefce0eff6a5fd"}, + {file = "ruff-0.1.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:28d920e319783d5303333630dae46ecc80b7ba294aeffedf946a02ac0b7cc3db"}, + {file = "ruff-0.1.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:104aa9b5e12cb755d9dce698ab1b97726b83012487af415a4512fedd38b1459e"}, + {file = "ruff-0.1.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1e63bf5a4a91971082a4768a0aba9383c12392d0d6f1e2be2248c1f9054a20da"}, + {file = "ruff-0.1.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d0738917c203246f3e275b37006faa3aa96c828b284ebfe3e99a8cb413c8c4b"}, + {file = "ruff-0.1.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:69dac82d63a50df2ab0906d97a01549f814b16bc806deeac4f064ff95c47ddf5"}, + {file = "ruff-0.1.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2aec598fb65084e41a9c5d4b95726173768a62055aafb07b4eff976bac72a592"}, + {file = "ruff-0.1.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:744dfe4b35470fa3820d5fe45758aace6269c578f7ddc43d447868cfe5078bcb"}, + {file = "ruff-0.1.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:479ca4250cab30f9218b2e563adc362bd6ae6343df7c7b5a7865300a5156d5a6"}, + {file = "ruff-0.1.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:aa8344310f1ae79af9ccd6e4b32749e93cddc078f9b5ccd0e45bd76a6d2e8bb6"}, + {file = "ruff-0.1.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:837c739729394df98f342319f5136f33c65286b28b6b70a87c28f59354ec939b"}, + {file = "ruff-0.1.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:e6837202c2859b9f22e43cb01992373c2dbfeae5c0c91ad691a4a2e725392464"}, + {file = "ruff-0.1.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:331aae2cd4a0554667ac683243b151c74bd60e78fb08c3c2a4ac05ee1e606a39"}, + {file = "ruff-0.1.9-py3-none-win32.whl", hash = "sha256:8151425a60878e66f23ad47da39265fc2fad42aed06fb0a01130e967a7a064f4"}, + {file = "ruff-0.1.9-py3-none-win_amd64.whl", hash = "sha256:c497d769164df522fdaf54c6eba93f397342fe4ca2123a2e014a5b8fc7df81c7"}, + {file = "ruff-0.1.9-py3-none-win_arm64.whl", hash = "sha256:0e17f53bcbb4fff8292dfd84cf72d767b5e146f009cccd40c2fad27641f8a7a9"}, + {file = "ruff-0.1.9.tar.gz", hash = "sha256:b041dee2734719ddbb4518f762c982f2e912e7f28b8ee4fe1dee0b15d1b6e800"}, ] [[package]] @@ -825,6 +903,17 @@ files = [ {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] +[[package]] +name = "soupsieve" +version = "2.5" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.8" +files = [ + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, +] + [[package]] name = "sphinx" version = "7.2.6" @@ -842,7 +931,6 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" -importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.14" @@ -880,23 +968,62 @@ sphinx = "*" test = ["pytest", "pytest-cov"] [[package]] -name = "sphinx-rtd-theme" -version = "1.3.0" -description = "Read the Docs theme for Sphinx" +name = "sphinx-basic-ng" +version = "1.0.0b2" +description = "A modern skeleton for Sphinx themes." optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.7" files = [ - {file = "sphinx_rtd_theme-1.3.0-py2.py3-none-any.whl", hash = "sha256:46ddef89cc2416a81ecfbeaceab1881948c014b1b6e4450b815311a89fb977b0"}, - {file = "sphinx_rtd_theme-1.3.0.tar.gz", hash = "sha256:590b030c7abb9cf038ec053b95e5380b5c70d61591eb0b552063fbe7c41f0931"}, + {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, + {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, ] [package.dependencies] -docutils = "<0.19" -sphinx = ">=1.6,<8" -sphinxcontrib-jquery = ">=4,<5" +sphinx = ">=4.0" [package.extras] -dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] +docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] + +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +description = "Add a copy button to each of your code cells." +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, + {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, +] + +[package.dependencies] +sphinx = ">=1.8" + +[package.extras] +code-style = ["pre-commit (==2.12.1)"] +rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] + +[[package]] +name = "sphinx-design" +version = "0.5.0" +description = "A sphinx extension for designing beautiful, view size responsive web components." +optional = false +python-versions = ">=3.8" +files = [ + {file = "sphinx_design-0.5.0-py3-none-any.whl", hash = "sha256:1af1267b4cea2eedd6724614f19dcc88fe2e15aff65d06b2f6252cee9c4f4c1e"}, + {file = "sphinx_design-0.5.0.tar.gz", hash = "sha256:e8e513acea6f92d15c6de3b34e954458f245b8e761b45b63950f65373352ab00"}, +] + +[package.dependencies] +sphinx = ">=5,<8" + +[package.extras] +code-style = ["pre-commit (>=3,<4)"] +rtd = ["myst-parser (>=1,<3)"] +testing = ["myst-parser (>=1,<3)", "pytest (>=7.1,<8.0)", "pytest-cov", "pytest-regressions"] +theme-furo = ["furo (>=2023.7.0,<2023.8.0)"] +theme-pydata = ["pydata-sphinx-theme (>=0.13.0,<0.14.0)"] +theme-rtd = ["sphinx-rtd-theme (>=1.0,<2.0)"] +theme-sbt = ["sphinx-book-theme (>=1.0,<2.0)"] [[package]] name = "sphinxcontrib-apidoc" @@ -1031,6 +1158,20 @@ Sphinx = ">=5" lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] +[[package]] +name = "stevedore" +version = "5.1.0" +description = "Manage dynamic plugins for Python applications" +optional = false +python-versions = ">=3.8" +files = [ + {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"}, + {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"}, +] + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + [[package]] name = "termcolor" version = "2.3.0" @@ -1045,17 +1186,6 @@ files = [ [package.extras] tests = ["pytest", "pytest-cov"] -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - [[package]] name = "tornado" version = "6.3.3" @@ -1076,17 +1206,6 @@ files = [ {file = "tornado-6.3.3.tar.gz", hash = "sha256:e7d8db41c0181c80d76c982aacc442c0783a2c54d6400fe028954201a2e032fe"}, ] -[[package]] -name = "typing-extensions" -version = "4.8.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, -] - [[package]] name = "urllib3" version = "2.0.7" @@ -1106,40 +1225,25 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.24.5" +version = "20.25.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, - {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, + {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, + {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<4" +platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] -[[package]] -name = "zipp" -version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, - {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - [metadata] lock-version = "2.0" -python-versions = "^3.9" -content-hash = "056633409f3b35d857e03a8fb91bd5bb465c8f3106e52c223b53e534e5b0edc2" +python-versions = "^3.11" +content-hash = "8a5fdc47f0299b588a91ad444b9dffb9cdfdfc7ae7be82655332f8807e6cfb97" diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 0000000..ab1033b --- /dev/null +++ b/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/pyproject.toml b/pyproject.toml index 91d0a09..b13218e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,59 +1,67 @@ +[build-system] +build-backend = "poetry.core.masonry.api" +requires = ["poetry-core"] + [tool.poetry] name = "f-lib" version = "0.0.0" -description = "Project TBA." authors = ["Kyle Finley "] -maintainers = [] -license = "Apache-2.0" -readme = "README.md" -homepage = "https://f-lib.readthedocs.io" -repository = "https://github.com/ITProKyle/f-lib" -documentation = "https://f-lib.readthedocs.io" -keywords = [] classifiers = [ "Intended Audience :: Developers", - "Topic :: Utilities", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Utilities", ] +description = "Python library created by Kyle Finley, for Kyle Finley." +documentation = "https://f-lib.readthedocs.io" +homepage = "https://f-lib.readthedocs.io" +keywords = [] +license = "Apache-2.0" +maintainers = [] packages = [ - { include = "f_lib" }, + {include = "f_lib"}, ] - -[tool.poetry.urls] -"Bug Tracker" = "https://github.com/ITProKyle/f-lib/issues" +readme = "README.md" +repository = "https://github.com/ITProKyle/f-lib" [tool.poetry.dependencies] -python = "^3.9" +python = "^3.11" +platformdirs = "^4.1.0" [tool.poetry.group.dev.dependencies] -pre-commit = "^3.4.0" +pre-commit = "^3.6.0" + +[tool.poetry.group.docs.dependencies] +doc8 = "^1.1.1" +furo = "^2023.9.10" +sphinx = "^7.2.6" +sphinx-autobuild = "^2021.3.14" +sphinx-copybutton = "^0.5.2" +sphinx-design = "^0.5.0" +sphinxcontrib-apidoc = "^0.4.0" +sphinxcontrib-jquery = "^4.1" [tool.poetry.group.lint.dependencies] -black = "^23.9.1" -ruff = "^0.0.291" +black = "^23.12.0" +ruff = "^0.1.9" [tool.poetry.group.test.dependencies] -pytest = "^7.4.2" +pytest = "^7.4.3" pytest-cov = "^4.1.0" -pytest-mock = "^3.11.1" +pytest-mock = "^3.12.0" +pytest-subprocess = "^1.5.0" pytest-sugar = "^0.9.7" +pytest-xdist = "^3.3.1" +[[tool.poetry.source]] +name = "PyPI" +priority = "primary" -[tool.poetry.group.docs.dependencies] -sphinx = "^7.2.6" -sphinx-autobuild = "^2021.3.14" -sphinx-rtd-theme = "^1.3.0" -sphinxcontrib-apidoc = "^0.4.0" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" - +[tool.poetry.urls] +"Bug Tracker" = "https://github.com/ITProKyle/f-lib/issues" [tool.black] force-exclude = ''' @@ -69,18 +77,16 @@ force-exclude = ''' ''' include = '\.pyi?$' line-length = 88 -target-version = ["py311"] - +target-version = ["py311", "py312"] [tool.coverage.report] exclude_lines = [ + "@overload", "cov: ignore", # standard exclude comment "if TYPE_CHECKING:", # excluded blocks "if __name__ == .__main__.:", "raise AssertionError", # defensive exceptions "raise NotImplementedError", - "from pathlib import Path", - "@overload", ] fail_under = 100 precision = 2 @@ -88,20 +94,29 @@ show_missing = true [tool.coverage.run] branch = false # not correctly reported - https://github.com/nedbat/coveragepy/issues/605 - +omit = [ + "*/type_defs.py", +] [tool.doc8] ignore = [ "D001", # Line too long ] +[tool.poetry-dynamic-versioning] # poetry self add "poetry-dynamic-versioning[plugin]" +bump = true +enable = true +fix-shallow-repository = true +metadata = false +strict = true +style = "pep440" [tool.pyright] exclude = [ - "**/__pycache__", "**/.eggs", "**/.git", "**/.venv", + "**/__pycache__", "**/docs", "**/node_modules", "**/typings", @@ -122,7 +137,6 @@ typeCheckingMode = "strict" useLibraryCodeForTypes = true venv = ".venv" - [tool.pytest.ini_options] addopts = [ "--cov-config=pyproject.toml", @@ -138,7 +152,6 @@ python_files = ["test_*.py"] python_functions = ["test_*"] testpaths = ["tests"] - [tool.ruff] # https://beta.ruff.rs/docs/settings/#top-level force-exclude = true ignore = [ @@ -153,33 +166,50 @@ ignore = [ "D407", # Missing dashed underline after section "D408", # Section underline should be in the line following the section's name "D409", # Section underline should match the length of its name - "ERA001", # Found commented-out code # NOTE(kyle): incorrectly detects cspell + "ERA001", # Found commented-out code # NOTE (kyle): incorrectly detects cspell "FIX002", # Line contains TODO "TD003", # Missing issue link on the line following this TODO "TID252", # Relative imports from parent modules are banned ] ignore-init-module-imports = true line-length = 120 -show-fixes = true select = ["ALL"] +show-fixes = true target-version = "py311" - [tool.ruff.extend-per-file-ignores] - "*.py" = [ - "PYI024", # Use `typing.NamedTuple` instead of `collections.namedtuple` - should only apply to pyi - ] - - [tool.ruff.flake8-type-checking] # https://beta.ruff.rs/docs/settings/#flake8-type-checking - runtime-evaluated-base-classes = [ - "pydantic.BaseModel", - "pydantic.BeforeValidator", - ] - - [tool.ruff.pep8-naming] # https://beta.ruff.rs/docs/settings/#pep8-naming - classmethod-decorators = [ - "pydantic.field_validator", - "pydantic.model_validator", - ] - - [tool.ruff.pydocstyle] # https://beta.ruff.rs/docs/settings/#pydocstyle - convention = "google" +[tool.ruff.extend-per-file-ignores] +"*.py" = [ + "PYI024", # Use `typing.NamedTuple` instead of `collections.namedtuple` - should only apply to pyi +] +"tests/*" = [ + "FBT001", # Boolean positional arg in function definition - this is fine here + "FBT003", # Boolean positional value in function call - this is fine here + "PT004", # Fixture does not return anything, add leading underscore + "S101", # Use of `assert` detected - this is fine here + "S108", # Probable insecure usage of temporary file or directory + "S604", # Function call with `shell=True` parameter identified - this is fine here + "SLF001", # Private member accessed - fine in tests +] + +[tool.ruff.flake8-type-checking] # https://beta.ruff.rs/docs/settings/#flake8-type-checking +runtime-evaluated-base-classes = [ + "pydantic.BaseModel", + "pydantic.BeforeValidator", +] + +[tool.ruff.pydocstyle] # https://beta.ruff.rs/docs/settings/#pydocstyle +convention = "google" + +[tool.ruff.pylint] # https://beta.ruff.rs/docs/settings/#pylint +allow-magic-value-types = ["bytes", "int", "str"] +max-args = 15 +max-returns = 10 + +[tool.tomlsort] +all = true +in_place = true +sort_first = ["tool.poetry"] +spaces_before_inline_comment = 2 +trailing_comma_inline_array = true +overrides."tool.poetry".first = ["name", "version"] +overrides."tool.poetry.dependencies".first = ["python"] diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..af5086f --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,27 @@ +"""Pytest configuration, fixtures, and plugins.""" +from __future__ import annotations + +import os +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest + +if TYPE_CHECKING: + from collections.abc import Iterator + + +@pytest.fixture() +def cd_tmp_path(tmp_path: Path) -> Iterator[Path]: + """Change directory to a temporary path. + + Returns: + Path: Temporary path object. + + """ + prev_dir = Path.cwd() + os.chdir(tmp_path) + try: + yield tmp_path + finally: + os.chdir(prev_dir) diff --git a/tests/test_placeholder.py b/tests/test_placeholder.py deleted file mode 100644 index feed646..0000000 --- a/tests/test_placeholder.py +++ /dev/null @@ -1,5 +0,0 @@ -"""A placeholder for tests.""" - - -def test_placeholder() -> None: - """Placeholder for tests.""" diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..e0310a0 --- /dev/null +++ b/tests/unit/__init__.py @@ -0,0 +1 @@ +"""Unit tests.""" diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py new file mode 100644 index 0000000..50e8298 --- /dev/null +++ b/tests/unit/conftest.py @@ -0,0 +1,37 @@ +"""Pytest configuration, fixtures, and plugins.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from f_lib import Environment + +if TYPE_CHECKING: + from pathlib import Path + + from pytest_mock import MockerFixture + + +@pytest.fixture() +def environment(tmp_path: Path) -> Environment: + """Create a deploy environment that can be used for testing.""" + return Environment(root_dir=tmp_path) + + +@pytest.fixture() +def platform_darwin(mocker: MockerFixture) -> None: + """Patch platform.system to always return "Darwin".""" + mocker.patch("platform.system", return_value="Darwin") + + +@pytest.fixture() +def platform_linux(mocker: MockerFixture) -> None: + """Patch platform.system to always return "Linux".""" + mocker.patch("platform.system", return_value="Linux") + + +@pytest.fixture() +def platform_windows(mocker: MockerFixture) -> None: + """Patch platform.system to always return "Windows".""" + mocker.patch("platform.system", return_value="Windows") diff --git a/tests/unit/mixins/__init__.py b/tests/unit/mixins/__init__.py new file mode 100644 index 0000000..997d196 --- /dev/null +++ b/tests/unit/mixins/__init__.py @@ -0,0 +1 @@ +"""Test f_lib.mixins.""" diff --git a/tests/unit/mixins/test__cli_interface.py b/tests/unit/mixins/test__cli_interface.py new file mode 100644 index 0000000..dfcf0f1 --- /dev/null +++ b/tests/unit/mixins/test__cli_interface.py @@ -0,0 +1,204 @@ +"""Test f_lib.mixins._cli_interface.""" +from __future__ import annotations + +import subprocess +from pathlib import Path +from typing import TYPE_CHECKING, Any +from unittest.mock import Mock + +import pytest + +from f_lib.mixins._cli_interface import CliInterfaceMixin + +if TYPE_CHECKING: + from pytest_mock import MockerFixture + from pytest_subprocess import FakeProcess + + from f_lib import Environment + +MODULE = "f_lib.mixins._cli_interface" + + +class TestCliInterfaceMixin: + """Test CliInterfaceMixin.""" + + class Kls(CliInterfaceMixin): + """Used in tests.""" + + def __init__(self, cwd: Path, environment: Environment) -> None: + """Instantiate class.""" + self.cwd = cwd + self.env = environment + + @pytest.mark.parametrize("env", [None, {"foo": "bar"}]) + def test__run_command( + self, env: dict[str, str] | None, mocker: MockerFixture, tmp_path: Path + ) -> None: + """Test _run_command.""" + ctx_env = {"foo": "bar", "bar": "foo"} + mock_subprocess = mocker.patch( + f"{MODULE}.subprocess.check_output", return_value="success" + ) + assert ( + self.Kls(tmp_path, Mock(vars=ctx_env))._run_command("test", env=env) + == mock_subprocess.return_value + ) + mock_subprocess.assert_called_once_with( + "test", + cwd=tmp_path, + env=env or ctx_env, + shell=True, + stderr=subprocess.STDOUT, + text=True, + ) + + def test__run_command_capture_output( + self, + environment: Environment, + fake_process: FakeProcess, + tmp_path: Path, + ) -> None: + """Test _run_command with capture_output.""" + fake_process.register_subprocess( + "test", + returncode=0, + stdout="\x1b[33msuccess\x1b[39m", # cspell: disable-line + ) + assert ( + self.Kls(tmp_path, environment)._run_command( + "test", suppress_output=False, capture_output=True + ) + == "success" + ) + + def test__run_command_capture_output_called_process_error( + self, + environment: Environment, + fake_process: FakeProcess, + tmp_path: Path, + ) -> None: + """Test _run_command with capture_output.""" + fake_process.register_subprocess( + "test", returncode=1, stdout="\x1b[33mfail\x1b[39m" # cspell: disable-line + ) + with pytest.raises(subprocess.CalledProcessError) as excinfo: + self.Kls(tmp_path, environment)._run_command( + "test", suppress_output=False, capture_output=True + ) + assert excinfo.value.returncode == 1 + assert excinfo.value.output == "fail" + + def test__run_command_no_suppress_output( + self, mocker: MockerFixture, tmp_path: Path + ) -> None: + """Test _run_command.""" + env = {"foo": "bar"} + mock_convert_list_to_shell_str = mocker.patch( + f"{MODULE}.convert_list_to_shell_str", return_value="success" + ) + mock_subprocess = mocker.patch( + f"{MODULE}.subprocess.check_call", return_value=0 + ) + assert not self.Kls(tmp_path, Mock(vars=env))._run_command( + ["foo", "bar"], suppress_output=False + ) + mock_convert_list_to_shell_str.assert_called_once_with(["foo", "bar"]) + mock_subprocess.assert_called_once_with( + mock_convert_list_to_shell_str.return_value, + cwd=tmp_path, + env=env, + shell=True, + ) + + @pytest.mark.parametrize("return_value", [False, True]) + def test_found_in_path(self, mocker: MockerFixture, return_value: bool) -> None: + """Test found_in_path.""" + exe = mocker.patch.object(self.Kls, "EXECUTABLE", "foo.exe", create=True) + mock_which = Mock(return_value=return_value) + mocker.patch(f"{MODULE}.shutil", which=mock_which) + assert self.Kls.found_in_path() is return_value + mock_which.assert_called_once_with(exe) + + @pytest.mark.parametrize( + ("provided", "expected"), + [ + ({}, []), + ({"is_flag": True}, ["--is-flag"]), + ({"is_flag": False}, []), + ({"key": "val", "is-flag": True}, ["--key", "val", "--is-flag"]), + ({"user": ["foo", "bar"]}, ["--user", "foo", "--user", "bar"]), + ( + {"file": [Path("/tmp/foo"), Path("/tmp/bar")]}, + ["--file", "/tmp/foo", "--file", "/tmp/bar"], + ), + ], + ) + def test_generate_command( + self, + expected: list[str], + mocker: MockerFixture, + provided: dict[str, Any], + ) -> None: + """Test generate_command.""" + exe = mocker.patch.object(self.Kls, "EXECUTABLE", "test.exe", create=True) + assert self.Kls.generate_command("command", **provided) == [ + exe, + "command", + *expected, + ] + + @pytest.mark.parametrize( + ("provided", "expected"), + [ + ({}, []), + ({"is_flag": True}, ["--is-flag"]), + ({"is_flag": False}, []), + ({"key": "val", "is-flag": True}, ["--key", "val", "--is-flag"]), + ({"user": ["foo", "bar"]}, ["--user", "foo", "--user", "bar"]), + ( + {"file": [Path("/tmp/foo"), Path("/tmp/bar")]}, + ["--file", "/tmp/foo", "--file", "/tmp/bar"], + ), + ], + ) + def test_generate_command_list( + self, + expected: list[str], + mocker: MockerFixture, + provided: dict[str, Any], + ) -> None: + """Test generate_command.""" + exe = mocker.patch.object(self.Kls, "EXECUTABLE", "test.exe", create=True) + assert self.Kls.generate_command(["command", "arg"], **provided) == [ + exe, + "command", + "arg", + *expected, + ] + + @pytest.mark.parametrize( + ("provided", "expected"), + [ + ({}, []), + ({"is_flag": True}, ["--is-flag"]), + ({"is_flag": False}, []), + ({"key": "val", "is-flag": True}, ["--key", "val", "--is-flag"]), + ({"user": ["foo", "bar"]}, ["--user", "foo", "--user", "bar"]), + ( + {"file": [Path("/tmp/foo"), Path("/tmp/bar")]}, + ["--file", "/tmp/foo", "--file", "/tmp/bar"], + ), + ], + ) + def test_generate_command_none( + self, + expected: list[str], + mocker: MockerFixture, + provided: dict[str, Any], + ) -> None: + """Test generate_command.""" + exe = mocker.patch.object(self.Kls, "EXECUTABLE", "test.exe", create=True) + assert self.Kls.generate_command(**provided) == [ + exe, + *expected, + ] diff --git a/tests/unit/mixins/test__del_cached_prop.py b/tests/unit/mixins/test__del_cached_prop.py new file mode 100644 index 0000000..91958fa --- /dev/null +++ b/tests/unit/mixins/test__del_cached_prop.py @@ -0,0 +1,37 @@ +"""Test f_lib.mixins._del_cached_prop.""" +from __future__ import annotations + +from functools import cached_property + +from f_lib.mixins._del_cached_prop import DelCachedPropMixin + + +class TestDelCachedPropMixin: + """Test DelCachedPropMixin.""" + + class Kls(DelCachedPropMixin): + """Used in tests.""" + + counter = 0 + + @cached_property + def test_prop(self) -> str: + """Test property.""" + self.counter += 1 + return "foobar" + + def test__del_cached_property(self) -> None: + """Test _del_cached_property.""" + obj = self.Kls() + # ensure suppression is working as expected + assert obj.counter == 0 + assert not obj._del_cached_property("test_prop") + assert obj.test_prop == "foobar" + assert obj.counter == 1 + # ensure value is cached and not being evaluated each call + assert obj.test_prop == "foobar" + assert obj.counter == 1 + # this would fail if the suppresion was outside the loop + assert not obj._del_cached_property("invalid", "test_prop") + assert obj.test_prop == "foobar" + assert obj.counter == 2 diff --git a/tests/unit/test__environment.py b/tests/unit/test__environment.py new file mode 100644 index 0000000..404244c --- /dev/null +++ b/tests/unit/test__environment.py @@ -0,0 +1,99 @@ +"""Test f_lib._environment.""" +from __future__ import annotations + +import os +from typing import TYPE_CHECKING + +from f_lib._environment import Environment + +if TYPE_CHECKING: + from pathlib import Path + +MODULE = "f_lib._environment" + + +class TestEnvironment: + """Test Environment.""" + + def test___init__(self, cd_tmp_path: Path) -> None: + """Test attributes set by init.""" + new_dir = cd_tmp_path / "new_dir" + obj = Environment( + environ={"key": "val"}, + root_dir=new_dir, + ) + + assert obj.root_dir == new_dir + assert obj.vars == {"key": "val"} + + def test___init___defaults(self, cd_tmp_path: Path) -> None: + """Test attributes set by init default values.""" + obj = Environment() + + assert obj.root_dir == cd_tmp_path + assert obj.vars == os.environ + + def test___init___emptry_environ(self) -> None: + """Test attributes set by init.""" + assert Environment(environ={}).vars == {} + + def test_ci_deleter(self) -> None: + """Test ``@ci.deleter``.""" + obj = Environment(environ={"CI": "1"}) + del obj.ci + assert not obj.ci + assert "CI" not in obj.vars + + def test_ci_setter(self) -> None: + """Test ``@ci.setter``.""" + obj = Environment(environ={}) + obj.ci = True + assert obj.ci + assert obj.vars["CI"] == "1" + + obj.ci = False + assert not obj.ci + assert "CI" not in obj.vars + + def test_ci_unset(self) -> None: + """Test ci.""" + assert not Environment(environ={}).ci + + def test_copy(self, tmp_path: Path) -> None: + """Test copy.""" + obj = Environment(root_dir=tmp_path) + obj_copy = obj.copy() + + assert obj_copy != obj + assert obj_copy.root_dir == obj.root_dir + assert obj_copy.vars == obj.vars + + def test_debug_setter(self) -> None: + """Test ``@debug.setter``.""" + obj = Environment(environ={}) + obj.debug = True + assert obj.debug + assert obj.vars["DEBUG"] == "1" + + obj.debug = False + assert not obj.debug + assert "DEBUG" not in obj.vars + + def test_debug_unset(self) -> None: + """Test debug.""" + assert not Environment(environ={}).debug + + def test_verbose_setter(self) -> None: + """Test ``@verbose.setter``.""" + obj = Environment(environ={}) + obj.verbose = True + assert obj.verbose + assert obj.vars["VERBOSE"] == "1" + + obj.verbose = False + assert not obj.verbose + assert "VERBOSE" not in obj.vars + + def test_verbose_unset(self) -> None: + """Test verbose.""" + assert not Environment(environ={}).verbose diff --git a/tests/unit/test__os_info.py b/tests/unit/test__os_info.py new file mode 100644 index 0000000..7091368 --- /dev/null +++ b/tests/unit/test__os_info.py @@ -0,0 +1,131 @@ +"""Test f_lib._os_info.""" +# ruff: noqa: ARG002 +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import Mock + +import pytest + +from f_lib._os_info import OsInfo + +if TYPE_CHECKING: + from pathlib import Path + + from pytest_mock import MockerFixture + +MODULE = "f_lib._os_info" + + +@pytest.fixture() +def clear_os_info() -> None: + """Clear OsInfo singleton.""" + OsInfo.clear_singleton() + + +@pytest.mark.usefixtures("clear_os_info") +class TestOsInfo: + """Test OsInfo.""" + + def test___platform_dirs_darwin( + self, mocker: MockerFixture, platform_darwin: None + ) -> None: + """Test _platform_dirs macOS.""" + mock_unix = mocker.patch(f"{MODULE}.Unix", return_value="success") + mock_windows = mocker.patch(f"{MODULE}.Windows") + + assert OsInfo()._platform_dirs == mock_unix.return_value + mock_windows.assert_not_called() + mock_unix.assert_called_once_with(appname="f-lib", appauthor="finley") + + def test___platform_dirs_linux( + self, mocker: MockerFixture, platform_linux: None + ) -> None: + """Test _platform_dirs Linux.""" + mock_unix = mocker.patch(f"{MODULE}.Unix", return_value="success") + mock_windows = mocker.patch(f"{MODULE}.Windows") + + assert OsInfo()._platform_dirs == mock_unix.return_value + mock_windows.assert_not_called() + mock_unix.assert_called_once_with(appname="f-lib", appauthor="finley") + + def test___platform_dirs_windows( + self, mocker: MockerFixture, platform_windows: None + ) -> None: + """Test _platform_dirs Windows.""" + mock_unix = mocker.patch(f"{MODULE}.Unix") + mock_windows = mocker.patch(f"{MODULE}.Windows", return_value="success") + + assert OsInfo()._platform_dirs == mock_windows.return_value + mock_windows.assert_called_once_with(appname="f-lib", appauthor="finley") + mock_unix.assert_not_called() + + def test_is_darwin_false(self, platform_linux: None) -> None: + """Test is_darwin False.""" + assert not OsInfo().is_darwin + + def test_is_darwin(self, platform_darwin: None) -> None: + """Test is_darwin.""" + assert OsInfo().is_darwin + + def test_is_linux_false(self, platform_darwin: None) -> None: + """Test is_linux False.""" + assert not OsInfo().is_linux + + def test_is_linux(self, platform_linux: None) -> None: + """Test is_linux.""" + assert OsInfo().is_linux + + def test_is_macos_false(self, platform_linux: None) -> None: + """Test is_macos False.""" + assert not OsInfo().is_macos + + def test_is_macos(self, platform_darwin: None) -> None: + """Test is_macos.""" + assert OsInfo().is_macos + + def test_is_posix_false(self, mocker: MockerFixture) -> None: + """Test is_posix False.""" + mock_os = mocker.patch(f"{MODULE}.os") + mock_os.name = "nt" + assert not OsInfo().is_posix + + def test_is_posix(self, mocker: MockerFixture) -> None: + """Test is_posix.""" + mock_os = mocker.patch(f"{MODULE}.os") + mock_os.name = "posix" + assert OsInfo().is_posix + + def test_is_windows_false(self, platform_linux: None) -> None: + """Test is_windows False.""" + assert not OsInfo().is_windows + + def test_is_windows(self, platform_windows: None) -> None: + """Test is_windows.""" + assert OsInfo().is_windows + + def test_name_darwin(self, platform_darwin: None) -> None: + """Test name darwin.""" + assert OsInfo().name == "darwin" + + def test_name_linux(self, platform_linux: None) -> None: + """Test name linux.""" + assert OsInfo().name == "linux" + + def test_name_windows(self, platform_windows: None) -> None: + """Test name windows.""" + assert OsInfo().name == "windows" + + def test_singleton(self) -> None: + """Test singleton.""" + assert id(OsInfo()) == id(OsInfo()) + + def test_user_config_dir(self, mocker: MockerFixture, tmp_path: Path) -> None: + """Test user_config_dir.""" + mocker.patch.object(OsInfo, "_platform_dirs", Mock(user_config_dir=tmp_path)) + assert OsInfo().user_config_dir == tmp_path + + def test_user_data_dir(self, mocker: MockerFixture, tmp_path: Path) -> None: + """Test user_data_dir.""" + mocker.patch.object(OsInfo, "_platform_dirs", Mock(user_data_dir=tmp_path)) + assert OsInfo().user_data_dir == tmp_path diff --git a/tests/unit/test__system_info.py b/tests/unit/test__system_info.py new file mode 100644 index 0000000..5673b0b --- /dev/null +++ b/tests/unit/test__system_info.py @@ -0,0 +1,116 @@ +"""Test f_lib._system_info.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from f_lib._os_info import OsInfo +from f_lib._system_info import SystemInfo, UnknownPlatformArchitectureError + +if TYPE_CHECKING: + from pytest_mock import MockerFixture + +MODULE = "f_lib._system_info" + + +@pytest.fixture() +def clear_system_info() -> None: + """Clear OsInfo singleton.""" + SystemInfo.clear_singleton() + + +@pytest.mark.usefixtures("clear_system_info") +class TestSystemInfo: + """Test SystemInfo.""" + + @pytest.mark.parametrize( + ("expected", "is_64bit", "is_arm", "is_x86"), + [ + ("amd32", False, False, True), + ("amd64", True, False, True), + ("arm32", False, True, False), + ("arm64", True, True, False), + ], + ) + def test_architecture( + self, + expected: str, + is_64bit: bool, + is_arm: bool, + is_x86: bool, + mocker: MockerFixture, + ) -> None: + """Test architecture.""" + mocker.patch.object(SystemInfo, "is_64bit", is_64bit) + mocker.patch.object(SystemInfo, "is_arm", is_arm) + mocker.patch.object(SystemInfo, "is_x86", is_x86) + assert SystemInfo().architecture == expected + + def test_architecture_unknown(self, mocker: MockerFixture) -> None: + """Test architecture.""" + mocker.patch.object(SystemInfo, "is_64bit", False) + mocker.patch.object(SystemInfo, "is_arm", False) + mocker.patch.object(SystemInfo, "is_x86", False) + with pytest.raises(UnknownPlatformArchitectureError): + SystemInfo().architecture # noqa: B018 + + @pytest.mark.parametrize( + ("expected", "maxsize"), [(False, 2**33), (True, 2**32)] + ) + def test_is_32bit( + self, expected: bool, maxsize: int, mocker: MockerFixture + ) -> None: + """Test is_32bit.""" + mocker.patch(f"{MODULE}.sys.maxsize", maxsize) + assert SystemInfo().is_32bit is expected + + @pytest.mark.parametrize( + ("expected", "maxsize"), [(False, 2**32), (True, 2**33)] + ) + def test_is_64bit( + self, expected: bool, maxsize: int, mocker: MockerFixture + ) -> None: + """Test is_64bit.""" + mocker.patch(f"{MODULE}.sys.maxsize", maxsize) + assert SystemInfo().is_64bit is expected + + @pytest.mark.parametrize( + ("expected", "machine"), [(False, "leg"), (True, "arm"), (True, "ARM")] + ) + def test_is_arm(self, expected: bool, machine: str, mocker: MockerFixture) -> None: + """Test is_arm.""" + platform_machine = mocker.patch( + f"{MODULE}.platform.machine", return_value=machine + ) + assert SystemInfo().is_arm is expected + platform_machine.assert_called_once_with() + + def test_is_frozen_false(self) -> None: + """Test is_frozen False.""" + assert not SystemInfo().is_frozen + + def test_is_frozen(self, mocker: MockerFixture) -> None: + """Test is_frozen.""" + mocker.patch(f"{MODULE}.sys.frozen", True, create=True) + assert SystemInfo().is_frozen + + def test_os(self) -> None: + """Test os.""" + assert isinstance(SystemInfo().os, OsInfo) + + def test_singleton(self) -> None: + """Test singleton.""" + assert id(SystemInfo()) == id(SystemInfo()) + + @pytest.mark.parametrize( + ("expected", "machine"), + [(False, "arm"), (True, "AMD64"), (True, "i386"), (True, "x86_64")], + ) + def test_is_x86(self, expected: bool, machine: str, mocker: MockerFixture) -> None: + """Test is_x86.""" + platform_machine = mocker.patch( + f"{MODULE}.platform.machine", return_value=machine + ) + assert SystemInfo().is_x86 is expected + platform_machine.assert_called_once_with() diff --git a/tests/unit/utils/__init__.py b/tests/unit/utils/__init__.py new file mode 100644 index 0000000..cc26feb --- /dev/null +++ b/tests/unit/utils/__init__.py @@ -0,0 +1 @@ +"""Test f_lib.utils.""" diff --git a/tests/unit/utils/test___init__.py b/tests/unit/utils/test___init__.py new file mode 100644 index 0000000..aea3cd4 --- /dev/null +++ b/tests/unit/utils/test___init__.py @@ -0,0 +1,78 @@ +"""Test f_lib.utils.__init__.""" +from __future__ import annotations + +from pathlib import Path +from typing import TYPE_CHECKING, Any + +import pytest + +from f_lib.utils import ( + convert_kwargs_to_shell_list, + convert_list_to_shell_str, + convert_to_cli_flag, +) + +if TYPE_CHECKING: + from pytest_mock import MockerFixture + +MODULE = "f_lib.utils" + + +@pytest.mark.parametrize( + ("provided", "expected"), + [ + ({}, []), + ({"is_flag": True}, ["--is-flag"]), + ({"is_flag": False}, []), + ({"key": "val", "is-flag": True}, ["--key", "val", "--is-flag"]), + ({"user": ["foo", "bar"]}, ["--user", "foo", "--user", "bar"]), + ( + {"file": [Path("/tmp/foo"), Path("/tmp/bar")]}, + ["--file", "/tmp/foo", "--file", "/tmp/bar"], + ), + ], +) +def test_convert_kwargs_to_shell_list( + expected: list[str], provided: dict[str, Any] +) -> None: + """Test convert_kwargs_to_shell_list.""" + assert convert_kwargs_to_shell_list(**provided) == expected + + +@pytest.mark.parametrize("platform", ["Darwin", "Linux"]) +def test_convert_list_to_shell_str(mocker: MockerFixture, platform: str) -> None: + """Test convert_list_to_shell_str.""" + mock_list2cmdline = mocker.patch(f"{MODULE}.subprocess.list2cmdline") + mocker.patch("platform.system", return_value=platform) + mock_join = mocker.patch("shlex.join", return_value="success") + assert convert_list_to_shell_str("foo") == mock_join.return_value + mock_list2cmdline.assert_not_called() + mock_join.assert_called_once_with("foo") + + +def test_convert_list_to_shell_str_windows(mocker: MockerFixture) -> None: + """Test convert_list_to_shell_str on Windows systems.""" + mock_list2cmdline = mocker.patch( + f"{MODULE}.subprocess.list2cmdline", return_value="success" + ) + mocker.patch("platform.system", return_value="Windows") + mock_join = mocker.patch("shlex.join") + assert convert_list_to_shell_str("foo") == mock_list2cmdline.return_value + mock_list2cmdline.assert_called_once_with("foo") + mock_join.assert_not_called() + + +@pytest.mark.parametrize( + ("prefix", "provided", "expected"), + [ + (None, "foo", "--foo"), + ("-", "foo_bar", "-foo-bar"), + ("--", "foo-bar", "--foo-bar"), + ], +) +def test_convert_to_cli_flag(expected: str, prefix: str | None, provided: str) -> None: + """Test convert_to_cli_flag.""" + if prefix: + assert convert_to_cli_flag(provided, prefix=prefix) == expected + else: + assert convert_to_cli_flag(provided) == expected