From 049b52c5fe9711856ce6fd9937b8589898d6de00 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Sun, 22 Jun 2025 21:52:51 +0200 Subject: [PATCH 01/46] Migrate to dataclasses in finecode_extension_api and use pydantic only to validate them, not for declaration to avoid unneeded dependencies. Initial version of execution environments. Use class for action definition instead of type alias to make it compatible with Python <3.12 --- .../fine_python_flake8/action.py | 2 + .../fine_python_mypy/action.py | 6 +- finecode.sh | 1 - finecode_dev_common_preset/poetry.lock | 191 +++++++----------- finecode_dev_common_preset/pyproject.toml | 2 +- .../finecode_extension_api/actions/format.py | 15 +- .../actions/ide/text_document_code_action.py | 7 +- .../actions/ide/text_document_inlay_hint.py | 12 +- .../finecode_extension_api/actions/lint.py | 20 +- .../finecode_extension_api/code_action.py | 27 ++- .../finecode_extension_api/common_types.py | 14 +- .../fine_python_format/preset.toml | 12 +- presets/fine_python_format/pyproject.toml | 2 +- .../fine_python_lint/preset.toml | 9 +- src/finecode/extension_runner/cli.py | 5 +- src/finecode/extension_runner/lsp_server.py | 5 +- src/finecode/extension_runner/services.py | 45 +---- src/finecode/extension_runner/start.py | 4 +- .../config/collect_actions.py | 2 + .../workspace_manager/config/config_models.py | 2 + .../workspace_manager/config/read_configs.py | 98 ++++++++- src/finecode/workspace_manager/context.py | 3 +- src/finecode/workspace_manager/domain.py | 27 ++- .../workspace_manager/finecode_cmd.py | 13 +- .../lsp_server/endpoints/action_tree.py | 33 +-- .../lsp_server/endpoints/diagnostics.py | 18 +- .../lsp_server/endpoints/document_sync.py | 30 +-- .../lsp_server/lsp_server.py | 4 +- src/finecode/workspace_manager/proxy_utils.py | 60 +++--- .../workspace_manager/runner/manager.py | 157 +++++++++----- .../workspace_manager/runner/runner_info.py | 16 +- src/finecode/workspace_manager/services.py | 148 ++++++++++---- .../__testdata__/list_ws/cli_tool/finecode.sh | 1 - tests/__testdata__/list_ws/ui_app/finecode.sh | 1 - .../nested_package/pyback/finecode.sh | 1 - 35 files changed, 616 insertions(+), 377 deletions(-) delete mode 100644 finecode.sh delete mode 100644 tests/__testdata__/list_ws/cli_tool/finecode.sh delete mode 100644 tests/__testdata__/list_ws/ui_app/finecode.sh delete mode 100644 tests/__testdata__/nested_package/pyback/finecode.sh diff --git a/extensions/fine_python_flake8/fine_python_flake8/action.py b/extensions/fine_python_flake8/fine_python_flake8/action.py index 630ad32..8e54c4c 100644 --- a/extensions/fine_python_flake8/fine_python_flake8/action.py +++ b/extensions/fine_python_flake8/fine_python_flake8/action.py @@ -2,6 +2,7 @@ import argparse import ast +import dataclasses import operator from pathlib import Path @@ -106,6 +107,7 @@ def run_flake8_on_single_file( return lint_messages +@dataclasses.dataclass class Flake8LintHandlerConfig(code_action.ActionHandlerConfig): max_line_length: int = 79 extend_select: list[str] | None = None diff --git a/extensions/fine_python_mypy/fine_python_mypy/action.py b/extensions/fine_python_mypy/fine_python_mypy/action.py index 51e4efa..4a7265b 100644 --- a/extensions/fine_python_mypy/fine_python_mypy/action.py +++ b/extensions/fine_python_mypy/fine_python_mypy/action.py @@ -1,6 +1,7 @@ # TODO: what to do with file manager? Mypy would need ability to check module text, # not only module file import asyncio +import dataclasses import hashlib import sys from pathlib import Path @@ -20,6 +21,7 @@ class DmypyFailedError(Exception): ... +@dataclasses.dataclass class MypyManyCodeActionConfig(code_action.ActionHandlerConfig): ... @@ -239,7 +241,7 @@ async def _run_dmypy(self, file_paths: list[Path], cwd: Path) -> str: self.logger.debug(f"run dmypy in {cwd}") status_file_path = self._get_status_file_path(dmypy_cwd=cwd) runner_python_executable = sys.executable - file_paths_str = " ".join([f"'{str(file_path)}'" for file_path in file_paths]) + file_paths_strs = [str(file_path) for file_path in file_paths] cmd_parts = [ f"{runner_python_executable}", "-m", @@ -248,7 +250,7 @@ async def _run_dmypy(self, file_paths: list[Path], cwd: Path) -> str: "run", "--", *self.DMYPY_ARGS, - f"{file_paths_str}", + *file_paths_strs ] cmd = " ".join(cmd_parts) dmypy_run_process = await self.command_runner.run( diff --git a/finecode.sh b/finecode.sh deleted file mode 100644 index 176acc2..0000000 --- a/finecode.sh +++ /dev/null @@ -1 +0,0 @@ -poetry run python \ No newline at end of file diff --git a/finecode_dev_common_preset/poetry.lock b/finecode_dev_common_preset/poetry.lock index 7c0956b..0fd78ba 100644 --- a/finecode_dev_common_preset/poetry.lock +++ b/finecode_dev_common_preset/poetry.lock @@ -79,14 +79,14 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "click" -version = "8.1.8" +version = "8.2.1" description = "Composable command line interface toolkit" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, + {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, + {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, ] [package.dependencies] @@ -129,129 +129,99 @@ name = "fine-python-ast" version = "0.1.0" description = "" optional = false -python-versions = ">=3.11, < 3.14" +python-versions = "<3.14,>=3.11" groups = ["main"] -files = [] -develop = false +files = [ + {file = "fine_python_ast-0.1.0-py3-none-any.whl", hash = "sha256:3dcdcdc40ed89e0b90686f2ad5d358d8edba622a5de4c1f52f2f0787dcc4e07d"}, + {file = "fine_python_ast-0.1.0.tar.gz", hash = "sha256:ebc68aef7d0379f8771f610e436f6075b7c299a8895f1b9b7769fd038f95397e"}, +] [package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" -subdirectory = "extensions/fine_python_ast" +finecode_extension_api = "0.1.0" [[package]] name = "fine-python-black" version = "0.1.0" description = "" optional = false -python-versions = ">=3.11, < 3.14" +python-versions = "<3.14,>=3.11" groups = ["main"] -files = [] -develop = false +files = [ + {file = "fine_python_black-0.1.0-py3-none-any.whl", hash = "sha256:27a012123783e4217222546e37280660e5a4948cdd3aa12d7fae7a3ce21a875d"}, + {file = "fine_python_black-0.1.0.tar.gz", hash = "sha256:ce300e897b4d819abe4754177cd5b81d29b2d4d04233942135edc1a8ed234bf7"}, +] [package.dependencies] black = ">=25.1.0,<26.0.0" -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" -subdirectory = "extensions/fine_python_black" +finecode_extension_api = "0.1.0" [[package]] name = "fine-python-flake8" version = "0.1.0" description = "" optional = false -python-versions = ">=3.11, < 3.14" +python-versions = "<3.14,>=3.11" groups = ["main"] -files = [] -develop = false +files = [ + {file = "fine_python_flake8-0.1.0-py3-none-any.whl", hash = "sha256:7ed7641505936f334396631f779953a13ae18c8f207a8258d722e50fa452d792"}, + {file = "fine_python_flake8-0.1.0.tar.gz", hash = "sha256:ccba9a1ec41f5f3aa756efa63bd64650237ad63d3098aedf673e9c254e502914"}, +] [package.dependencies] -fine_python_ast = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_ast"} -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} +fine_python_ast = "0.1.0" +finecode_extension_api = "0.1.0" flake8 = ">=7.1.2,<8.0.0" types-flake8 = ">=7.1.0.20241020,<8.0.0.0" -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" -subdirectory = "extensions/fine_python_flake8" - [[package]] name = "fine-python-format" version = "0.1.0" description = "" optional = false -python-versions = ">=3.11, < 3.14" +python-versions = "<3.14,>=3.11" groups = ["main"] -files = [] -develop = false +files = [ + {file = "fine_python_format-0.1.0-py3-none-any.whl", hash = "sha256:26315bc8ae5a4830efd9ee41ab3e220beead52c0702cc6f82df720e59dbfbfc2"}, + {file = "fine_python_format-0.1.0.tar.gz", hash = "sha256:8d8031316abed4761d096c2a0c100c8d7cc6d391ea386149ef625964ab80d0d2"}, +] [package.dependencies] -fine_python_black = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_black"} -fine_python_isort = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_isort"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" -subdirectory = "presets/fine_python_format" +fine_python_black = "0.1.0" +fine_python_isort = "0.1.0" [[package]] name = "fine-python-isort" version = "0.1.0" description = "" optional = false -python-versions = ">= 3.11, < 3.14" +python-versions = "<3.14,>=3.11" groups = ["main"] -files = [] -develop = false +files = [ + {file = "fine_python_isort-0.1.0-py3-none-any.whl", hash = "sha256:cbd6cd5502d65122e9f6461758f78db8d9e5628ab97c41a67ee6ef85a3526c8e"}, + {file = "fine_python_isort-0.1.0.tar.gz", hash = "sha256:64468a96b49663226b422885b25de7c22c24f6dafef6c25c1d02ea6e49662b53"}, +] [package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} +finecode_extension_api = "0.1.0" isort = ">=5.13,<6" -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" -subdirectory = "extensions/fine_python_isort" - [[package]] name = "fine-python-lint" version = "0.1.0" description = "" optional = false -python-versions = ">=3.11, < 3.14" +python-versions = "<3.14,>=3.11" groups = ["main"] -files = [] -develop = false +files = [ + {file = "fine_python_lint-0.1.0-py3-none-any.whl", hash = "sha256:180517a54a38ab4942bf51611d03a9afd3668e8d11f59e289444c2e1756434de"}, + {file = "fine_python_lint-0.1.0.tar.gz", hash = "sha256:597660e54fe4fa4024b54f0050cdc378c48d0a03fd2938d1c0ae60204a8160d8"}, +] [package.dependencies] -fine_python_flake8 = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_flake8"} -fine_python_mypy = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_mypy"} +fine_python_flake8 = "0.1.0" +fine_python_mypy = "0.1.0" flake8-bugbear = ">=24.12.12,<25.0.0" -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" -subdirectory = "presets/fine_python_lint" - [[package]] name = "fine-python-module-exports" version = "0.1.0" @@ -263,14 +233,14 @@ files = [] develop = false [package.dependencies] -fine_python_ast = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_ast"} -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} +fine_python_ast = "0.1.0" +finecode_extension_api = "0.1.0" [package.source] type = "git" url = "https://github.com/finecode-dev/finecode.git" reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" +resolved_reference = "fa081a6f836fb6ca46611fc1d9561ba74ad16686" subdirectory = "extensions/fine_python_module_exports" [[package]] @@ -284,58 +254,45 @@ files = [] develop = false [package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} +finecode_extension_api = "0.1.0" mypy = ">=1.15,<2.0" [package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" -subdirectory = "extensions/fine_python_mypy" +type = "directory" +url = "../extensions/fine_python_mypy" [[package]] name = "fine-python-recommended" version = "0.1.0" description = "" optional = false -python-versions = ">=3.11, < 3.14" +python-versions = "<3.14,>=3.11" groups = ["main"] -files = [] -develop = false +files = [ + {file = "fine_python_recommended-0.1.0-py3-none-any.whl", hash = "sha256:3416df1eb4af8ce80206684c87c55dc55331999ef8b9ffb60a874daf0ddfbb98"}, + {file = "fine_python_recommended-0.1.0.tar.gz", hash = "sha256:bc721e5eb381b02a6049f171e042c29cbbd9dac5ecec8c22a88806a6811d3b9d"}, +] [package.dependencies] -fine_python_format = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_format"} -fine_python_lint = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_lint"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" -subdirectory = "presets/fine_python_recommended" +fine_python_format = "0.1.0" +fine_python_lint = "0.1.0" [[package]] name = "finecode-extension-api" version = "0.1.0" description = "" optional = false -python-versions = ">=3.11, < 3.14" +python-versions = "<3.14,>=3.11" groups = ["main"] -files = [] -develop = false +files = [ + {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, + {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, +] [package.dependencies] pydantic = ">=2.10.6,<3.0.0" typing-extensions = ">=4.12.2,<5.0.0" -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "946844b2f151a7091664c407f7e24ce01442bee5" -subdirectory = "finecode_extension_api" - [[package]] name = "flake8" version = "7.2.0" @@ -490,14 +447,14 @@ files = [ [[package]] name = "platformdirs" -version = "4.3.7" +version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, - {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, + {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, + {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, ] [package.extras] @@ -519,14 +476,14 @@ files = [ [[package]] name = "pydantic" -version = "2.11.4" +version = "2.11.5" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, - {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, + {file = "pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7"}, + {file = "pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a"}, ] [package.dependencies] @@ -680,14 +637,14 @@ types-pyflakes = "*" [[package]] name = "types-pyflakes" -version = "3.3.2.20250429" +version = "3.3.2.20250511" description = "Typing stubs for pyflakes" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "types_pyflakes-3.3.2.20250429-py3-none-any.whl", hash = "sha256:f9ccc1968ddd1a18232c1e66cfcce8a9e8f4b2b85fbbf682bf87148a2b2d58a0"}, - {file = "types_pyflakes-3.3.2.20250429.tar.gz", hash = "sha256:a81b0ee91e34d143f655d366bd4002730f0e342a5aa338779d2f995515ce1c5c"}, + {file = "types_pyflakes-3.3.2.20250511-py3-none-any.whl", hash = "sha256:85802fdd0b64d3553ef12ac0ba02d85c4bbd38747579c544e6bb005ec455becf"}, + {file = "types_pyflakes-3.3.2.20250511.tar.gz", hash = "sha256:d0ef58f9ec15eab2a9e427814f48587be4eb2752a8ae7dec201d65086f50ace2"}, ] [[package]] @@ -704,14 +661,14 @@ files = [ [[package]] name = "typing-inspection" -version = "0.4.0" +version = "0.4.1" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, - {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, + {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, + {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, ] [package.dependencies] @@ -720,4 +677,4 @@ typing-extensions = ">=4.12.0" [metadata] lock-version = "2.1" python-versions = ">=3.11, < 3.14" -content-hash = "21456805d44cb3d018f182a4d345d7e8165871f4ee1f7c41b70e755cbd0a3a7f" +content-hash = "986f7e2322de03c2f24845d27748b9a3cb4bab269efe8e91ba627714b92e8dfc" diff --git a/finecode_dev_common_preset/pyproject.toml b/finecode_dev_common_preset/pyproject.toml index cd59846..ec45326 100644 --- a/finecode_dev_common_preset/pyproject.toml +++ b/finecode_dev_common_preset/pyproject.toml @@ -7,7 +7,7 @@ readme = "README.md" requires-python = ">=3.11, < 3.14" dependencies = [ "fine_python_aksem @ git+https://github.com/Aksem/fine_python_aksem.git", - "fine_python_recommended @ git+https://github.com/finecode-dev/finecode.git#subdirectory=presets/fine_python_recommended", + "fine_python_recommended==0.1.0", ] [tool.poetry] diff --git a/finecode_extension_api/finecode_extension_api/actions/format.py b/finecode_extension_api/finecode_extension_api/actions/format.py index c5da7cc..e227980 100644 --- a/finecode_extension_api/finecode_extension_api/actions/format.py +++ b/finecode_extension_api/finecode_extension_api/actions/format.py @@ -1,3 +1,4 @@ +import dataclasses import sys from pathlib import Path from typing import NamedTuple @@ -12,6 +13,7 @@ from finecode_extension_api import code_action, textstyler +@dataclasses.dataclass class FormatRunPayload(code_action.RunActionPayload): file_paths: list[Path] save: bool @@ -22,6 +24,7 @@ class FileInfo(NamedTuple): file_version: str + class FormatRunContext(code_action.RunActionContext): def __init__( self, @@ -42,12 +45,14 @@ async def init(self, initial_payload: FormatRunPayload) -> None: ) -class FormatRunFileResult(code_action.RunActionResult): +@dataclasses.dataclass +class FormatRunFileResult: changed: bool # changed code or empty string if code was not changed code: str +@dataclasses.dataclass class FormatRunResult(code_action.RunActionResult): result_by_file_path: dict[Path, FormatRunFileResult] @@ -79,11 +84,13 @@ def to_text(self) -> str | textstyler.StyledText: return text -type FormatAction = code_action.Action[ - FormatRunPayload, FormatRunContext, FormatRunResult -] +class FormatAction(code_action.Action): + PAYLOAD_TYPE = FormatRunPayload + RUN_CONTEXT_TYPE = FormatRunContext + RESULT_TYPE = FormatRunResult +@dataclasses.dataclass class SaveFormatHandlerConfig(code_action.ActionHandlerConfig): ... diff --git a/finecode_extension_api/finecode_extension_api/actions/ide/text_document_code_action.py b/finecode_extension_api/finecode_extension_api/actions/ide/text_document_code_action.py index 84bf6cb..f57b52c 100644 --- a/finecode_extension_api/finecode_extension_api/actions/ide/text_document_code_action.py +++ b/finecode_extension_api/finecode_extension_api/actions/ide/text_document_code_action.py @@ -1,8 +1,10 @@ +import dataclasses import enum from finecode_extension_api import code_action, common_types +@dataclasses.dataclass class CodeActionPayload(code_action.RunActionPayload): text_document: common_types.TextDocumentIdentifier range: common_types.Range @@ -27,10 +29,11 @@ class CodeActionTriggerKind(enum.IntEnum): AUTOMATIC = 2 -class Diagnostic(code_action.BaseModel): ... +@dataclasses.dataclass +class Diagnostic: ... -class CodeActionContext(code_action.BaseModel): +class CodeActionContext: diagnostics: list[Diagnostic] only: CodeActionKind | None trigger_kind: CodeActionTriggerKind diff --git a/finecode_extension_api/finecode_extension_api/actions/ide/text_document_inlay_hint.py b/finecode_extension_api/finecode_extension_api/actions/ide/text_document_inlay_hint.py index b2523d7..44fb9f5 100644 --- a/finecode_extension_api/finecode_extension_api/actions/ide/text_document_inlay_hint.py +++ b/finecode_extension_api/finecode_extension_api/actions/ide/text_document_inlay_hint.py @@ -1,8 +1,10 @@ +import dataclasses import enum from finecode_extension_api import code_action, common_types +@dataclasses.dataclass class InlayHintPayload(code_action.RunActionPayload): text_document: common_types.TextDocumentIdentifier range: common_types.Range @@ -13,7 +15,8 @@ class InlayHintKind(enum.IntEnum): PARAM = 2 -class InlayHint(code_action.BaseModel): +@dataclasses.dataclass +class InlayHint: position: common_types.Position label: str kind: InlayHintKind @@ -21,10 +24,11 @@ class InlayHint(code_action.BaseModel): padding_right: bool = False +@dataclasses.dataclass class InlayHintResult(code_action.RunActionResult): hints: list[InlayHint] | None -type TextDocumentInlayHintAction = code_action.Action[ - InlayHintPayload, code_action.RunActionContext, InlayHintResult -] +class TextDocumentInlayHintAction(code_action.Action): + PAYLOAD_TYPE = InlayHintPayload + RESULT_TYPE = InlayHintResult diff --git a/finecode_extension_api/finecode_extension_api/actions/lint.py b/finecode_extension_api/finecode_extension_api/actions/lint.py index 34046d0..39f5510 100644 --- a/finecode_extension_api/finecode_extension_api/actions/lint.py +++ b/finecode_extension_api/finecode_extension_api/actions/lint.py @@ -1,16 +1,19 @@ import collections.abc +import dataclasses import enum from pathlib import Path from finecode_extension_api import code_action, textstyler -class Position(code_action.BaseModel): +@dataclasses.dataclass +class Position: line: int character: int -class Range(code_action.BaseModel): +@dataclasses.dataclass +class Range: start: Position end: Position @@ -23,7 +26,8 @@ class LintMessageSeverity(enum.IntEnum): HINT = 4 -class LintMessage(code_action.BaseModel): +@dataclasses.dataclass +class LintMessage: range: Range message: str code: str | None = None @@ -32,6 +36,7 @@ class LintMessage(code_action.BaseModel): severity: LintMessageSeverity | None = None +@dataclasses.dataclass class LintRunPayload(code_action.RunActionPayload, collections.abc.AsyncIterable): file_paths: list[Path] @@ -39,6 +44,7 @@ def __aiter__(self) -> collections.abc.AsyncIterator[Path]: return LintRunPayloadIterator(self) +@dataclasses.dataclass class LintRunPayloadIterator(collections.abc.AsyncIterator): def __init__(self, lint_run_payload: LintRunPayload): self.lint_run_payload = lint_run_payload @@ -54,6 +60,7 @@ async def __anext__(self) -> Path: return self.lint_run_payload.file_paths[self.current_file_path_index - 1] +@dataclasses.dataclass class LintRunResult(code_action.RunActionResult): # messages is a dict to support messages for multiple files because it could be the # case that linter checks given file and its dependencies. @@ -100,6 +107,7 @@ def return_code(self) -> code_action.RunReturnCode: return code_action.RunReturnCode.SUCCESS -type LintAction = code_action.Action[ - LintRunPayload, code_action.RunActionWithPartialResultsContext, LintRunResult -] +class LintAction(code_action.Action): + PAYLOAD_TYPE = LintRunPayload + RUN_CONTEXT_TYPE = code_action.RunActionWithPartialResultsContext + RESULT_TYPE = LintRunResult diff --git a/finecode_extension_api/finecode_extension_api/code_action.py b/finecode_extension_api/finecode_extension_api/code_action.py index 6d6efc4..25e4261 100644 --- a/finecode_extension_api/finecode_extension_api/code_action.py +++ b/finecode_extension_api/finecode_extension_api/code_action.py @@ -2,19 +2,22 @@ import asyncio import collections.abc +import dataclasses import enum from pathlib import Path from typing import Generic, Protocol, TypeVar +import typing -from pydantic import BaseModel -from finecode_extension_api import partialresultscheduler +from finecode_extension_api import partialresultscheduler, textstyler -class ActionHandlerConfig(BaseModel): ... +@dataclasses.dataclass +class ActionHandlerConfig: ... -class RunActionPayload(BaseModel): ... +@dataclasses.dataclass +class RunActionPayload: ... class RunReturnCode(enum.IntEnum): @@ -22,11 +25,12 @@ class RunReturnCode(enum.IntEnum): ERROR = 1 -class RunActionResult(BaseModel): +@dataclasses.dataclass +class RunActionResult: def update(self, other: RunActionResult) -> None: raise NotImplementedError() - def to_text(self) -> str: + def to_text(self) -> str | textstyler.StyledText: return str(self) @property @@ -77,7 +81,16 @@ def __init__(self, project_dir: Path, cache_dir: Path) -> None: self.cache_dir = cache_dir -class Action(Generic[RunPayloadType, RunContextType, RunResultType]): ... +@dataclasses.dataclass +class ActionConfig: + run_handlers_concurrently: bool = False + + +class Action(Generic[RunPayloadType, RunContextType, RunResultType]): + PAYLOAD_TYPE: typing.Type[RunActionPayload] = RunActionPayload + RUN_CONTEXT_TYPE: typing.Type[RunActionContext] = RunActionContext + RESULT_TYPE: typing.Type[RunActionResult] = RunActionResult + CONFIG: typing.Type[ActionConfig] = ActionConfig InitializeCallable = collections.abc.Callable[[], None] diff --git a/finecode_extension_api/finecode_extension_api/common_types.py b/finecode_extension_api/finecode_extension_api/common_types.py index 4fd0923..5a21f86 100644 --- a/finecode_extension_api/finecode_extension_api/common_types.py +++ b/finecode_extension_api/finecode_extension_api/common_types.py @@ -1,21 +1,25 @@ -from finecode_extension_api.code_action import BaseModel +import dataclasses -class Position(BaseModel): +@dataclasses.dataclass +class Position: line: int character: int -class Range(BaseModel): +@dataclasses.dataclass +class Range: start: Position end: Position -class TextDocumentIdentifier(BaseModel): +@dataclasses.dataclass +class TextDocumentIdentifier: uri: str -class TextDocumentItem(BaseModel): +@dataclasses.dataclass +class TextDocumentItem: uri: str language_id: str version: int diff --git a/presets/fine_python_format/fine_python_format/preset.toml b/presets/fine_python_format/fine_python_format/preset.toml index 06451ed..ec2babd 100644 --- a/presets/fine_python_format/fine_python_format/preset.toml +++ b/presets/fine_python_format/fine_python_format/preset.toml @@ -1,9 +1,15 @@ [tool.finecode.action.format] source = "finecode_extension_api.actions.format.FormatAction" handlers = [ - { name = "isort", source = "fine_python_isort.IsortFormatHandler" }, - { name = "black", source = "fine_python_black.BlackFormatHandler" }, - { name = "save", source = "finecode_extension_api.actions.format.SaveFormatHandler" }, + { name = "isort", source = "fine_python_isort.IsortFormatHandler", env = "dev_no_runtime", dependencies = [ + "fine_python_black==0.1.0", + ] }, + { name = "black", source = "fine_python_black.BlackFormatHandler", env = "dev_no_runtime", dependencies = [ + "fine_python_isort==0.1.0", + ] }, + { name = "save", source = "finecode_extension_api.actions.format.SaveFormatHandler", env = "dev_no_runtime", dependencies = [ + "finecode_extension_api==0.1.0", + ] }, ] [tool.finecode.action_handler.isort.config] diff --git a/presets/fine_python_format/pyproject.toml b/presets/fine_python_format/pyproject.toml index 5173275..b74b89f 100644 --- a/presets/fine_python_format/pyproject.toml +++ b/presets/fine_python_format/pyproject.toml @@ -5,7 +5,7 @@ description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["fine_python_black==0.1.0", "fine_python_isort==0.1.0"] +dependencies = ["fine_extension_api==0.1.*"] [tool.poetry.group.dev.dependencies] finecode = { version = "0.2.0" } diff --git a/presets/fine_python_lint/fine_python_lint/preset.toml b/presets/fine_python_lint/fine_python_lint/preset.toml index eae1276..978178e 100644 --- a/presets/fine_python_lint/fine_python_lint/preset.toml +++ b/presets/fine_python_lint/fine_python_lint/preset.toml @@ -1,8 +1,13 @@ [tool.finecode.action.lint] source = "finecode_extension_api.actions.lint.LintAction" handlers = [ - { name = "flake8", source = "fine_python_flake8.Flake8LintHandler" }, - { name = "mypy", source = "fine_python_mypy.MypyLintHandler" }, + { name = "flake8", source = "fine_python_flake8.Flake8LintHandler", env = "dev_no_runtime", dependencies = [ + "fine_python_flake8==0.1.0", + "flake8-bugbear (>=24.12.12,<25.0.0)", + ] }, + { name = "mypy", source = "fine_python_mypy.MypyLintHandler", env = "dev_no_runtime", dependencies = [ + "fine_python_mypy==0.1.0", + ] }, ] [tool.finecode.action_handler.flake8.config] diff --git a/src/finecode/extension_runner/cli.py b/src/finecode/extension_runner/cli.py index a24790d..d6e6674 100644 --- a/src/finecode/extension_runner/cli.py +++ b/src/finecode/extension_runner/cli.py @@ -18,7 +18,8 @@ type=click.Path(exists=True, file_okay=False, resolve_path=True, path_type=Path), required=True, ) -def main(trace: bool, debug: bool, debug_port: int, project_path: Path): +@click.option("--env-name", "env_name", type=str, default="unknown") +def main(trace: bool, debug: bool, debug_port: int, project_path: Path, env_name: str): if debug is True: import debugpy @@ -36,7 +37,7 @@ def main(trace: bool, debug: bool, debug_port: int, project_path: Path): # extension runner doesn't stop with async start after closing LS client(WM). Use # sync start until this problem is solved - runner_start.start_runner_sync() + runner_start.start_runner_sync(env_name) if __name__ == "__main__": diff --git a/src/finecode/extension_runner/lsp_server.py b/src/finecode/extension_runner/lsp_server.py index 9b48ca0..4f89f6d 100644 --- a/src/finecode/extension_runner/lsp_server.py +++ b/src/finecode/extension_runner/lsp_server.py @@ -6,6 +6,7 @@ from __future__ import annotations import atexit +import dataclasses import json import time @@ -113,8 +114,10 @@ def send_partial_result( token: int | str, partial_result: code_action.RunActionResult ) -> None: logger.debug(f"Send partial result for {token}") + partial_result_dict = dataclasses.asdict(partial_result) + partial_result_json = json.dumps(partial_result_dict) server.progress( - types.ProgressParams(token=token, value=partial_result.model_dump_json()) + types.ProgressParams(token=token, value=partial_result_json) ) services.document_requester = document_requester diff --git a/src/finecode/extension_runner/services.py b/src/finecode/extension_runner/services.py index a611deb..4160947 100644 --- a/src/finecode/extension_runner/services.py +++ b/src/finecode/extension_runner/services.py @@ -9,6 +9,7 @@ from pathlib import Path from loguru import logger +from pydantic.dataclasses import dataclass as pydantic_dataclass from finecode.extension_runner import bootstrap, context, domain, global_state from finecode.extension_runner import ( @@ -120,42 +121,12 @@ def create_action_exec_info(action: domain.Action) -> domain.ActionExecInfo: logger.error(f"Error importing action type: {e}") raise e - # typing.TypeAliasType is available in Python 3.12+ - if hasattr(typing, "TypeAliasType") and not isinstance( - action_type_def, typing.TypeAliasType - ): - raise Exception("Action definition expected to be a type") - - action_type_alias = action_type_def.__value__ - - if not isinstance(action_type_alias, typing._GenericAlias): - raise Exception( - "Action definition expected to be an instantiation of" - " finecode_extension_api.code_action.Action type" - ) - - try: - unpack_with_action = next(iter(action_type_alias)) - except StopIteration: - raise Exception("Action type definition is invalid: no action type alias?") - - # typing.Unpack cannot used in isinstance: - # TypeError: typing.Unpack cannot be used with isinstance() - # if not isinstance(unpack_with_action,typing.Unpack): - # raise Exception("Action type definition is invalid: type alias is not unpack") - - if len(unpack_with_action.__args__) != 1: - raise Exception("Action type definition is invalid: expected 1 Action instance") - - action_generic_alias = unpack_with_action.__args__[0] - action_args = action_generic_alias.__args__ - - if len(action_args) != 3: - raise Exception( - "Action type definition is invalid: Action type expects 3 arguments" - ) + if not issubclass(action_type_def, code_action.Action): + raise Exception("Action class expected to be a subclass of finecode_extension_api.code_action.Action") - payload_type, run_context_type, _ = action_args + payload_type = action_type_def.PAYLOAD_TYPE + run_context_type = action_type_def.RUN_CONTEXT_TYPE + # TODO: validate that classes and correct subclasses? @@ -288,7 +259,8 @@ async def run_action( # TODO: catch validation errors payload: code_action.RunActionPayload | None = None if action_exec_info.payload_type is not None: - payload = action_exec_info.payload_type(**request.params) + payload_type_with_validation = pydantic_dataclass(action_exec_info.payload_type) + payload = payload_type_with_validation(**request.params) run_context: code_action.RunActionContext | None = None if action_exec_info.run_context_type is not None: @@ -297,6 +269,7 @@ async def run_action( known_args={"run_id": lambda _: run_id}, params_to_ignore=["self"], ) + run_context = action_exec_info.run_context_type(**constructor_args) # TODO: handler errors await run_context.init(initial_payload=payload) diff --git a/src/finecode/extension_runner/start.py b/src/finecode/extension_runner/start.py index 6a7e967..866009f 100644 --- a/src/finecode/extension_runner/start.py +++ b/src/finecode/extension_runner/start.py @@ -54,7 +54,7 @@ # await pygls_server_utils.start_io_async(server) -def start_runner_sync(): +def start_runner_sync(env_name: str) -> None: project_log_dir_path = project_dirs.get_project_dir(global_state.project_dir_path) logger.remove() # disable logging raw messages @@ -63,7 +63,7 @@ def start_runner_sync(): # ~~extension runner communicates with workspace manager with tcp, we can print logs # to stdout as well~~. See README.md logs.save_logs_to_file( - file_path=project_log_dir_path / "execution.log", + file_path=project_log_dir_path / f"execution_{env_name}.log", log_level=global_state.log_level, stdout=False, ) diff --git a/src/finecode/workspace_manager/config/collect_actions.py b/src/finecode/workspace_manager/config/collect_actions.py index 7ee0543..9f2435e 100644 --- a/src/finecode/workspace_manager/config/collect_actions.py +++ b/src/finecode/workspace_manager/config/collect_actions.py @@ -52,6 +52,8 @@ def _collect_actions_in_config( .get("action_handler", {}) .get(handler.name, {}) .get("config", {}), + env=handler.env, + dependencies=handler.dependencies ) for handler in action_def.handlers ], diff --git a/src/finecode/workspace_manager/config/config_models.py b/src/finecode/workspace_manager/config/config_models.py index 96b8cbf..7cb9597 100644 --- a/src/finecode/workspace_manager/config/config_models.py +++ b/src/finecode/workspace_manager/config/config_models.py @@ -32,6 +32,8 @@ class PresetDefinition(BaseModel): class ActionHandlerDefinition(BaseModel): name: str source: str + env: str + dependencies: list[str] class ActionDefinition(BaseModel): diff --git a/src/finecode/workspace_manager/config/read_configs.py b/src/finecode/workspace_manager/config/read_configs.py index faaecc9..97b6432 100644 --- a/src/finecode/workspace_manager/config/read_configs.py +++ b/src/finecode/workspace_manager/config/read_configs.py @@ -31,7 +31,7 @@ async def read_projects_in_dir( logger.debug(f"Skip '{def_file}' because it is config dump, not real project config") continue - status = domain.ProjectStatus.READY + status = domain.ProjectStatus.CONFIG_VALID actions: list[domain.Action] | None = None with open(def_file, "rb") as pyproject_file: @@ -40,12 +40,6 @@ async def read_projects_in_dir( if project_def.get("tool", {}).get("finecode", None) is None: status = domain.ProjectStatus.NO_FINECODE actions = [] - else: - # finecode config exists, check also finecode.sh - finecode_sh_path = def_file.parent / "finecode.sh" - - if not finecode_sh_path.exists(): - status = domain.ProjectStatus.NO_FINECODE_SH new_project = domain.Project( name=def_file.parent.name, @@ -73,13 +67,26 @@ async def read_project_config( finecode_raw_config = project_def.get("tool", {}).get("finecode", None) if finecode_raw_config: finecode_config = config_models.FinecodeConfig(**finecode_raw_config) + # all presets expected to be in `dev_no_runtime` environment + project_runners = ws_context.ws_projects_extension_runners[project.dir_path] + # TODO: can it be the case that there is no such runner? + dev_no_runtime_runner = project_runners['dev_no_runtime'] new_config = await collect_config_from_py_presets( presets_sources=[preset.source for preset in finecode_config.presets], def_path=project.def_path, - runner=ws_context.ws_projects_extension_runners[project.dir_path], + runner=dev_no_runtime_runner, ) _merge_projects_configs(project_def, new_config) + # add builtins if they are not overwritten + prepare_envs_action = domain.Action(name='prepare_envs', source='finecode_extension_api.actions.prepare_envs.PrepareEnvsAction', handlers=[domain.ActionHandler(name='prepare_venvs', source='fine_python_virtualenv.VirtualenvPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_venv==0.1.*'])], config={}) + add_action_to_config_if_new(project_def, prepare_envs_action) + + # add runtime dependency group if it's not explicitly declared + add_runtime_dependency_group_if_new(project_def) + + merge_handlers_dependencies_into_groups(project_def) + ws_context.ws_projects_raw_configs[project.dir_path] = project_def else: logger.info( @@ -294,3 +301,78 @@ def _merge_preset_configs(config1: dict[str, Any], config2: dict[str, Any]) -> N del config2["tool"]["finecode"]["action_handler"] del config2["tool"]["finecode"] + + +def add_action_to_config_if_new(raw_config: dict[str, Any], action: domain.Action) -> None: + # adds action to raw config if it is not defined yet. Existing action will be not + # overwritten + tool_config = add_or_get_dict_key_value(raw_config, 'tool', {}) + finecode_config = add_or_get_dict_key_value(tool_config, 'finecode', {}) + action_config = add_or_get_dict_key_value(finecode_config, 'action', {}) + if action.name not in action_config: + action_raw_dict = { + "source": action.source, + "handlers": [handler_to_dict(handler) for handler in action.handlers] + } + action_config[action.name] = action_raw_dict + + # example of action definition: + # [tool.finecode.action.text_document_inlay_hint] + # source = "finecode_extension_api.actions.ide.text_document_inlay_hint.TextDocumentInlayHintAction" + # handlers = [ + # { name = 'module_exports_inlay_hint', source = 'fine_python_module_exports.extension.get_document_inlay_hints', env = "dev_no_runtime", dependencies = [ + # "fine_python_module_exports @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_module_exports", + # ] }, + # ] + + +def add_or_get_dict_key_value(dict_obj: dict[str, Any], key: str, default_value: Any) -> Any: + if key not in dict_obj: + value = default_value + dict_obj[key] = value + else: + value = dict_obj[key] + + return value + + +def handler_to_dict(handler: domain.ActionHandler) -> dict[str, str | list[str]]: + return { + "name": handler.name, + "source": handler.source, + "env": handler.env, + "dependencies": handler.dependencies + } + + +def add_runtime_dependency_group_if_new(project_config: dict[str, Any]) -> None: + runtime_dependencies = project_config.get('project', {}).get('dependencies', []) + + deps_groups = add_or_get_dict_key_value(project_config, 'dependency-groups', {}) + if 'runtime' not in deps_groups: + deps_groups['runtime'] = runtime_dependencies + + +def merge_handlers_dependencies_into_groups(project_config: dict[str, Any]) -> None: + # tool.finecode.action..handlers[x].dependencies + actions_dict = project_config.get('tool', {}).get('finecode', {}).get('action', {}) + if 'dependency-groups' not in project_config: + project_config['dependency-groups'] = {} + deps_groups = project_config['dependency-groups'] + + for action_info in actions_dict.values(): + action_handlers = action_info.get('handlers', []) + + for handler in action_handlers: + handler_env = handler.get('env', None) + if handler_env is None: + logger.warning(f'Handler {handler} has no env, skip it') + continue + deps = handler.get('dependencies', []) + + if handler_env not in deps_groups: + deps_groups[handler_env] = [] + + env_deps = deps_groups[handler_env] + # should we remove duplicates here? + env_deps += deps diff --git a/src/finecode/workspace_manager/context.py b/src/finecode/workspace_manager/context.py index 2cef2e7..f790b61 100644 --- a/src/finecode/workspace_manager/context.py +++ b/src/finecode/workspace_manager/context.py @@ -19,7 +19,8 @@ class WorkspaceContext: ws_projects: dict[Path, domain.Project] = field(default_factory=dict) # ws_projects_raw_configs: dict[Path, dict[str, Any]] = field(default_factory=dict) - ws_projects_extension_runners: dict[Path, ExtensionRunnerInfo] = field( + # > + ws_projects_extension_runners: dict[Path, dict[str, ExtensionRunnerInfo]] = field( default_factory=dict ) ignore_watch_paths: set[Path] = field(default_factory=set) diff --git a/src/finecode/workspace_manager/domain.py b/src/finecode/workspace_manager/domain.py index 0f98307..14e7ef9 100644 --- a/src/finecode/workspace_manager/domain.py +++ b/src/finecode/workspace_manager/domain.py @@ -4,6 +4,8 @@ from enum import Enum, auto from pathlib import Path +import ordered_set + class Preset: def __init__(self, source: str) -> None: @@ -11,10 +13,12 @@ def __init__(self, source: str) -> None: class ActionHandler: - def __init__(self, name: str, source: str, config: dict[str, typing.Any]): + def __init__(self, name: str, source: str, config: dict[str, typing.Any], env: str, dependencies: list[str]): self.name: str = name self.source: str = source self.config: dict[str, typing.Any] = config + self.env: str = env + self.dependencies: list[str] = dependencies class Action: @@ -55,15 +59,26 @@ def __str__(self) -> str: def __repr__(self) -> str: return str(self) + + @property + def envs(self) -> list[str]: + if self.actions is None: + raise ValueError("Actions are not collected yet") + + all_envs_set = ordered_set.OrderedSet([]) + for action in self.actions: + action_envs = [handler.env for handler in action.handlers] + all_envs_set |= ordered_set.OrderedSet(action_envs) + + return list(all_envs_set) class ProjectStatus(Enum): - READY = auto() + CONFIG_INVALID = auto() + # config valid, but no finecode in project NO_FINECODE = auto() - NO_FINECODE_SH = auto() - RUNNER_FAILED = auto() - RUNNING = auto() - EXITED = auto() + # config valid and finecode is used in project + CONFIG_VALID = auto() RootActions = list[str] diff --git a/src/finecode/workspace_manager/finecode_cmd.py b/src/finecode/workspace_manager/finecode_cmd.py index 5cd2114..92ff65f 100644 --- a/src/finecode/workspace_manager/finecode_cmd.py +++ b/src/finecode/workspace_manager/finecode_cmd.py @@ -1,13 +1,10 @@ from pathlib import Path -def get_finecode_cmd(project_path: Path) -> str: - sh_path = project_path / "finecode.sh" +def get_python_cmd(project_path: Path, env_name: str) -> str: + venv_python_path = project_path / ".venvs" / env_name / "bin" / "python" - if not sh_path.exists(): - raise ValueError(f"finecode.sh not found in project {project_path}") + if not venv_python_path.exists(): + raise ValueError(f"{env_name} venv not found in project {project_path}") - with open(sh_path, "r") as sh_file: - sh_cmd = sh_file.readline() - - return sh_cmd + return venv_python_path.as_posix() diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py b/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py index 955184a..cb50294 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py +++ b/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py @@ -2,6 +2,7 @@ from pathlib import Path from loguru import logger +import ordered_set from pygls.lsp.server import LanguageServer from finecode.workspace_manager import context, domain @@ -37,7 +38,7 @@ def get_project_action_tree( project: domain.Project, ws_context: context.WorkspaceContext ) -> list[schemas.ActionTreeNode]: actions_nodes: list[schemas.ActionTreeNode] = [] - if project.status == domain.ProjectStatus.RUNNING: + if project.status == domain.ProjectStatus.CONFIG_VALID: assert project.actions is not None for action in project.actions: node_id = f"{project.dir_path.as_posix()}::{action.name}" @@ -67,7 +68,7 @@ def get_project_action_tree( ) else: logger.info( - f"Project is not running: {project.dir_path}, no actions will be shown" + f"Project has no valid config and finecode: {project.dir_path}, no actions will be shown" ) return actions_nodes @@ -175,10 +176,11 @@ async def __list_actions( # list ws dirs and first level # wait for start of all runners, this is required to be able to resolve presets - all_started_coros = [ - runner.initialized_event.wait() - for runner in ws_context.ws_projects_extension_runners.values() - ] + all_started_coros = [] + for envs in ws_context.ws_projects_extension_runners.values(): + # all presets are expected to be in `dev_no_runtime` env + dev_no_runtime_runner = envs['dev_no_runtime'] + all_started_coros.append(dev_no_runtime_runner.initialized_event.wait()) await asyncio.gather(*all_started_coros) nodes: list[schemas.ActionTreeNode] = create_node_list_for_ws(ws_context) @@ -279,19 +281,22 @@ async def __reload_action(action_node_id: str) -> None: action_name = splitted_action_id[1] try: - next(action for action in project.actions if action.name == action_name) + action = next(action for action in project.actions if action.name == action_name) except StopIteration as error: logger.error(f"Unexpected error, project or action not found: {error}") raise InternalError() - runner = global_state.ws_context.ws_projects_extension_runners[project_path] + all_handlers_envs = ordered_set.OrderedSet([handler.env for handler in action.handlers]) + for env in all_handlers_envs: + # parallel to speed up? + runner = global_state.ws_context.ws_projects_extension_runners[project_path][env] - try: - await runner_client.reload_action(runner, action_name) - except runner_client.BaseRunnerRequestException as error: - await user_messages.error( - f"Action {action_name} reload failed: {error.message}" - ) + try: + await runner_client.reload_action(runner, action_name) + except runner_client.BaseRunnerRequestException as error: + await user_messages.error( + f"Action {action_name} reload failed: {error.message}" + ) async def run_action( diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py b/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py index 689b4c6..2f86de6 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py +++ b/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py @@ -8,6 +8,7 @@ from loguru import logger from lsprotocol import types +import ordered_set from finecode import pygls_types_utils from finecode.workspace_manager import domain, project_analyzer, proxy_utils @@ -332,10 +333,15 @@ async def _workspace_diagnostic( exec_info_by_project_dir_path: dict[Path, LintActionExecInfo] = {} for project_dir_path in relevant_projects_paths: - runner = global_state.ws_context.ws_projects_extension_runners[project_dir_path] - exec_info_by_project_dir_path[project_dir_path] = LintActionExecInfo( - runner=runner, action_name="lint" - ) + project = global_state.ws_context.ws_projects[project_dir_path] + action = next(action for action in project.actions if action.name == 'lint') + action_envs = ordered_set.OrderedSet([handler.env for handler in action.handlers]) + runners_by_env = global_state.ws_context.ws_projects_extension_runners[project_dir_path] + for env in action_envs: + runner = runners_by_env[env] + exec_info_by_project_dir_path[project_dir_path] = LintActionExecInfo( + runner=runner, action_name="lint" + ) # find which runner is responsible for which files # currently FineCode supports only raw python files, find them in each ws project @@ -351,9 +357,9 @@ async def _workspace_diagnostic( for project_dir_path, files_for_runner in files_by_projects.items(): project = global_state.ws_context.ws_projects[project_dir_path] - if project.status != domain.ProjectStatus.RUNNING: + if project.status != domain.ProjectStatus.CONFIG_VALID: logger.warning( - f"Runner of project {project_dir_path} is not running," + f"Project {project_dir_path} has not valid configuration and finecode," " lint in it will not be executed" ) continue diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/document_sync.py b/src/finecode/workspace_manager/lsp_server/endpoints/document_sync.py index 3c58020..5116830 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/document_sync.py +++ b/src/finecode/workspace_manager/lsp_server/endpoints/document_sync.py @@ -7,7 +7,7 @@ from finecode.workspace_manager import domain from finecode.workspace_manager.lsp_server import global_state -from finecode.workspace_manager.runner import runner_client +from finecode.workspace_manager.runner import runner_client, runner_info async def document_did_open( @@ -34,14 +34,15 @@ async def document_did_open( try: async with asyncio.TaskGroup() as tg: for project_path in projects_paths: - runner = global_state.ws_context.ws_projects_extension_runners[ + runners_by_env = global_state.ws_context.ws_projects_extension_runners[ project_path ] - tg.create_task( - runner_client.notify_document_did_open( - runner=runner, document_info=document_info + for runner in runners_by_env.values(): + tg.create_task( + runner_client.notify_document_did_open( + runner=runner, document_info=document_info + ) ) - ) except ExceptionGroup as e: logger.error(f"Error while sending opened document: {e}") @@ -62,21 +63,26 @@ async def document_did_close( projects_paths = [ project_path for project_path, project in global_state.ws_context.ws_projects.items() - if project.status == domain.ProjectStatus.RUNNING + if project.status == domain.ProjectStatus.CONFIG_VALID and file_path.is_relative_to(project_path) ] try: async with asyncio.TaskGroup() as tg: for project_path in projects_paths: - runner = global_state.ws_context.ws_projects_extension_runners[ + runners_by_env = global_state.ws_context.ws_projects_extension_runners[ project_path ] - tg.create_task( - runner_client.notify_document_did_close( - runner=runner, document_uri=params.text_document.uri + for runner in runners_by_env.values(): + if runner.status != runner_info.RunnerStatus.RUNNING: + logger.trace(f"Runner {runner.working_dir_path} is not running, skip it") + continue + + tg.create_task( + runner_client.notify_document_did_close( + runner=runner, document_uri=params.text_document.uri + ) ) - ) except ExceptionGroup as e: logger.error(f"Error while sending closed document: {e}") diff --git a/src/finecode/workspace_manager/lsp_server/lsp_server.py b/src/finecode/workspace_manager/lsp_server/lsp_server.py index 8894727..ce25ea1 100644 --- a/src/finecode/workspace_manager/lsp_server/lsp_server.py +++ b/src/finecode/workspace_manager/lsp_server/lsp_server.py @@ -263,14 +263,14 @@ async def reset(ls: LanguageServer, params): async def restart_extension_runner(ls: LanguageServer, params): - logger.info(f"restart extension runner {params}") + logger.info(f"restart extension runners {params}") await global_state.server_initialized.wait() params_dict = params[0] runner_working_dir_str = params_dict["projectPath"] runner_working_dir_path = Path(runner_working_dir_str) - await wm_services.restart_extension_runner( + await wm_services.restart_extension_runners( runner_working_dir_path, global_state.ws_context ) diff --git a/src/finecode/workspace_manager/proxy_utils.py b/src/finecode/workspace_manager/proxy_utils.py index ca0c206..339ce68 100644 --- a/src/finecode/workspace_manager/proxy_utils.py +++ b/src/finecode/workspace_manager/proxy_utils.py @@ -1,22 +1,20 @@ import asyncio import collections.abc import contextlib -from pathlib import Path +import pathlib from typing import Any from loguru import logger -from finecode.workspace_manager import context, domain, find_project +from finecode.workspace_manager import context, domain, find_project, services +from finecode.workspace_manager.services import ActionRunFailed from finecode.workspace_manager.runner import manager as runner_manager from finecode.workspace_manager.runner import runner_client, runner_info -class ActionRunFailed(Exception): ... - - -def find_action_project_runner( - file_path: Path, action_name: str, ws_context: context.WorkspaceContext -) -> runner_info.ExtensionRunnerInfo: +def find_action_project( + file_path: pathlib.Path, action_name: str, ws_context: context.WorkspaceContext +) -> pathlib.Path: try: project_path = find_project.find_project_with_action_for_file( file_path=file_path, @@ -32,36 +30,34 @@ def find_action_project_runner( raise ActionRunFailed(error) project_status = ws_context.ws_projects[project_path].status - if project_status != domain.ProjectStatus.RUNNING: + if project_status != domain.ProjectStatus.CONFIG_VALID: logger.info( - f"Extension runner {project_path} is not running, " + f"Extension runner {project_path} has no valid config with finecode, " f"status: {project_status.name}" ) raise ActionRunFailed( - f"Extension runner {project_path} is not running, " + f"Project {project_path} has no valid config with finecode," f"status: {project_status.name}" ) - runner = ws_context.ws_projects_extension_runners[project_path] - return runner + return project_path async def find_action_project_and_run( - file_path: Path, + file_path: pathlib.Path, action_name: str, params: dict[str, Any], ws_context: context.WorkspaceContext, ) -> runner_client.RunActionResponse: - runner = find_action_project_runner( + project_path = find_action_project( file_path=file_path, action_name=action_name, ws_context=ws_context ) + project = ws_context.ws_projects[project_path] + try: - response = await runner_client.run_action( - runner=runner, action_name=action_name, params=params - ) - except runner_client.BaseRunnerRequestException as error: - logger.error(f"Error on running action {action_name} on {file_path}: {error.message}") - raise ActionRunFailed(error.message) + response = await services.run_action(action_name=action_name, params=params, project_def=project, ws_context=ws_context, preprocess_payload=False) + except services.ActionRunFailed as exception: + raise exception return response @@ -133,7 +129,7 @@ async def run_action_and_notify( runner: runner_info.ExtensionRunnerInfo, result_list: AsyncList, partial_results_task: asyncio.Task, -) -> None: +) -> runner_client.RunActionResponse: try: return await run_action_in_runner( action_name=action_name, @@ -197,17 +193,17 @@ async def run_with_partial_results( @contextlib.asynccontextmanager async def find_action_project_and_run_with_partial_results( - file_path: Path, + file_path: pathlib.Path, action_name: str, params: dict[str, Any], partial_result_token: int | str, ws_context: context.WorkspaceContext, ) -> collections.abc.AsyncIterator[runner_client.RunActionRawResult]: logger.trace(f"Run {action_name} on {file_path}") - runner = find_action_project_runner( + project_path = find_action_project( file_path=file_path, action_name=action_name, ws_context=ws_context ) - + # TODO return run_with_partial_results( action_name=action_name, params=params, @@ -218,18 +214,18 @@ async def find_action_project_and_run_with_partial_results( def find_all_projects_with_action( action_name: str, ws_context: context.WorkspaceContext -) -> list[Path]: +) -> list[pathlib.Path]: projects = ws_context.ws_projects - relevant_projects: dict[Path, domain.Project] = { + relevant_projects: dict[pathlib.Path, domain.Project] = { path: project for path, project in projects.items() if project.status != domain.ProjectStatus.NO_FINECODE } - # exclude not running projects and projects without requested action + # exclude projects without valid config and projects without requested action for project_dir_path, project_def in relevant_projects.copy().items(): - if project_def.status != domain.ProjectStatus.RUNNING: - # projects that are not running, have no actions. Files of those projects + if project_def.status != domain.ProjectStatus.CONFIG_VALID: + # projects without valid config have no actions. Files of those projects # will be not processed because we don't know whether it has one of expected # actions continue @@ -243,7 +239,7 @@ def find_all_projects_with_action( del relevant_projects[project_dir_path] continue - relevant_projects_paths: list[Path] = list(relevant_projects.keys()) + relevant_projects_paths: list[pathlib.Path] = list(relevant_projects.keys()) return relevant_projects_paths @@ -251,4 +247,6 @@ def find_all_projects_with_action( "find_action_project_and_run", "find_action_project_and_run_with_partial_results", "run_with_partial_results", + # reexport for easier use of proxy helpers + "ActionRunFailed" ] diff --git a/src/finecode/workspace_manager/runner/manager.py b/src/finecode/workspace_manager/runner/manager.py index a5b0fb8..dce6d7b 100644 --- a/src/finecode/workspace_manager/runner/manager.py +++ b/src/finecode/workspace_manager/runner/manager.py @@ -65,15 +65,17 @@ def map_change_object(change): async def start_extension_runner( - runner_dir: Path, ws_context: context.WorkspaceContext + runner_dir: Path, env_name: str, ws_context: context.WorkspaceContext ) -> runner_info.ExtensionRunnerInfo | None: + runner_info_instance = runner_info.ExtensionRunnerInfo( + working_dir_path=runner_dir, env_name=env_name, status=runner_info.RunnerStatus.READY_TO_START, initialized_event=asyncio.Event(), client=None + ) + try: - _finecode_cmd = finecode_cmd.get_finecode_cmd(runner_dir) + python_cmd = finecode_cmd.get_python_cmd(runner_dir, env_name) except ValueError: try: - ws_context.ws_projects[runner_dir].status = ( - domain.ProjectStatus.NO_FINECODE_SH - ) + runner_info_instance.status = runner_info.RunnerStatus.NO_VENV await notify_project_changed(ws_context.ws_projects[runner_dir]) except KeyError: ... @@ -82,6 +84,7 @@ async def start_extension_runner( process_args: list[str] = [ "--trace", f"--project-path={runner_dir.as_posix()}", + f"--env-name={env_name}" ] # TODO: config parameter for debug and debug port # if runner_dir == Path("/home/user/Development/FineCode/finecode"): @@ -91,17 +94,15 @@ async def start_extension_runner( process_args_str: str = " ".join(process_args) client = await create_lsp_client_io( runner_info.CustomJsonRpcClient, - f"{_finecode_cmd} -m finecode.extension_runner.cli {process_args_str}", + f"{python_cmd} -m finecode.extension_runner.cli {process_args_str}", runner_dir, ) - runner_info_instance = runner_info.ExtensionRunnerInfo( - working_dir_path=runner_dir, initialized_event=asyncio.Event(), client=client - ) + runner_info_instance.client = client async def on_exit(): logger.debug(f"Extension Runner {runner_info_instance.working_dir_path} exited") - ws_context.ws_projects[runner_dir].status = domain.ProjectStatus.EXITED - await notify_project_changed(ws_context.ws_projects[runner_dir]) + runner_info_instance.status = runner_info.RunnerStatus.EXITED + await notify_project_changed(ws_context.ws_projects[runner_dir]) # TODO: fix # TODO: restart if WM is not stopping runner_info_instance.client.server_exit_callback = on_exit @@ -172,9 +173,10 @@ def stop_extension_runner_sync(runner: runner_info.ExtensionRunnerInfo) -> None: async def kill_extension_runner(runner: runner_info.ExtensionRunnerInfo) -> None: - if runner.client._server is not None: - runner.client._server.terminate() - await runner.client.stop() + if runner.client is not None: + if runner.client._server is not None: + runner.client._server.terminate() + await runner.client.stop() async def update_runners(ws_context: context.WorkspaceContext) -> None: @@ -186,34 +188,49 @@ async def update_runners(ws_context: context.WorkspaceContext) -> None: # # this function should handle all possible statuses of projects and they either # start of fail to start, only projects without finecode are ignored - extension_runners = list(ws_context.ws_projects_extension_runners.values()) + extension_runners_paths = list(ws_context.ws_projects_extension_runners.keys()) new_dirs, deleted_dirs = dirs_utils.find_changed_dirs( [*ws_context.ws_projects.keys()], - [runner.working_dir_path for runner in extension_runners], + extension_runners_paths, ) for deleted_dir in deleted_dirs: - try: - runner_to_delete = next( - runner - for runner in extension_runners - if runner.working_dir_path == deleted_dir - ) - except StopIteration: - continue - await stop_extension_runner(runner_to_delete) - extension_runners.remove(runner_to_delete) + runners_by_env = ws_context.ws_projects_extension_runners[deleted_dir] + for runner in runners_by_env.values(): + await stop_extension_runner(runner) + del ws_context.ws_projects_extension_runners[deleted_dir] new_runners_tasks: list[asyncio.Task] = [] try: + # first start runner in 'dev_no_runtime' env to be able to resolve presets for + # other envs async with asyncio.TaskGroup() as tg: for new_dir in new_dirs: project = ws_context.ws_projects[new_dir] project_status = project.status - if project_status == domain.ProjectStatus.READY: - runner_task = tg.create_task(start_extension_runner(runner_dir=new_dir, ws_context=ws_context)) - new_runners_tasks.append(runner_task) + if project_status == domain.ProjectStatus.CONFIG_VALID: + task = tg.create_task(_start_dev_no_runtime_runner(project_def=project, ws_context=ws_context)) + new_runners_tasks.append(task) elif project_status != domain.ProjectStatus.NO_FINECODE: - raise RunnerFailedToStart(f"Runner for project '{project.name}' failed to start, status: {project_status.name}") + raise RunnerFailedToStart(f"Project '{project.name}' has invalid configuration, status: {project_status.name}") + + save_runners_from_tasks_in_context(tasks=new_runners_tasks, ws_context=ws_context) + + # only then start runners for all other envs + new_runners_tasks = [] + async with asyncio.TaskGroup() as tg: + for new_dir in new_dirs: + project = ws_context.ws_projects[new_dir] + project_status = project.status + if ws_context.ws_projects_extension_runners.get(new_dir, {}).get('dev_no_runtime', None) is None: + # start only if dev_no_runtime started successfully + for env in project.envs: + if env == 'dev_no_runtime': + # this env has already started above + continue + + runner_task = tg.create_task(start_extension_runner(runner_dir=new_dir, env_name=env, ws_context=ws_context)) + new_runners_tasks.append(runner_task) + except ExceptionGroup as eg: for exception in eg.exceptions: if isinstance(exception, runner_client.BaseRunnerRequestException) or isinstance(exception, RunnerFailedToStart): @@ -222,14 +239,11 @@ async def update_runners(ws_context: context.WorkspaceContext) -> None: logger.exception(exception) raise RunnerFailedToStart("Failed to start runner") - extension_runners += [ + save_runners_from_tasks_in_context(tasks=new_runners_tasks, ws_context=ws_context) + extension_runners: list[runner_info.ExtensionRunnerInfo] = [ runner.result() for runner in new_runners_tasks if runner is not None ] - ws_context.ws_projects_extension_runners = { - runner.working_dir_path: runner for runner in extension_runners - } - try: async with asyncio.TaskGroup() as tg: for runner in extension_runners: @@ -249,6 +263,31 @@ async def update_runners(ws_context: context.WorkspaceContext) -> None: raise RunnerFailedToStart("Failed to initialize runner") +async def _start_dev_no_runtime_runner(project_def: domain.Project, ws_context: context.WorkspaceContext) -> runner_info.ExtensionRunnerInfo: + runner = await start_extension_runner(runner_dir=project_def.dir_path, env_name='dev_no_runtime', ws_context=ws_context) + + if runner is None: + raise Exception("Runner failed to start") + + save_runner_in_context(runner=runner, ws_context=ws_context) + + # we cannot reuse '_init_runner' here because we need to start lsp client first, + # read config(=also resolve presets) and only then we can update runner config, + # because this requires resolved project config with presets + await _init_lsp_client(runner=runner, project=project_def) + + + await read_configs.read_project_config(project=project_def, ws_context=ws_context) + collect_actions.collect_actions( + project_path=project_def.dir_path, ws_context=ws_context + ) + + await _update_runner_config(runner=runner, project=project_def) + await _finish_runner_init(runner=runner, project=project_def, ws_context=ws_context) + + return runner + + async def _init_runner( runner: runner_info.ExtensionRunnerInfo, project: domain.Project, @@ -256,6 +295,15 @@ async def _init_runner( ) -> None: # initialization is required to be able to perform other requests logger.trace(f"Init runner {runner.working_dir_path}") + assert project.actions is not None + + await _init_lsp_client(runner=runner, project=project) + + await _update_runner_config(runner=runner, project=project) + await _finish_runner_init(runner=runner, project=project, ws_context=ws_context) + + +async def _init_lsp_client(runner: runner_info.ExtensionRunnerInfo, project: domain.Project) -> None: try: await runner_client.initialize( runner, @@ -264,7 +312,7 @@ async def _init_runner( client_version="0.1.0", ) except runner_client.BaseRunnerRequestException as error: - project.status = domain.ProjectStatus.RUNNER_FAILED + runner.status = runner_info.RunnerStatus.FAILED await notify_project_changed(project) runner.initialized_event.set() raise RunnerFailedToStart(f"Runner failed to initialize: {error.message}") @@ -273,7 +321,7 @@ async def _init_runner( await runner_client.notify_initialized(runner) except Exception as error: logger.error(f"Failed to notify runner about initialization: {error}") - project.status = domain.ProjectStatus.RUNNER_FAILED + runner.status = runner_info.RunnerStatus.FAILED await notify_project_changed(project) runner.initialized_event.set() logger.exception(error) @@ -281,21 +329,15 @@ async def _init_runner( f"Runner failed to notify about initialization: {error}" ) - logger.debug("LSP Server initialized") - - await read_configs.read_project_config(project=project, ws_context=ws_context) - collect_actions.collect_actions( - project_path=project.dir_path, ws_context=ws_context - ) + logger.debug("LSP Client initialized") - assert ( - project.actions is not None - ), f"Actions of project {project.dir_path} are not read yet" +async def _update_runner_config(runner: runner_info.ExtensionRunnerInfo, project: domain.Project) -> None: + assert project.actions is not None try: await runner_client.update_config(runner, project.actions) except runner_client.BaseRunnerRequestException as exception: - project.status = domain.ProjectStatus.RUNNER_FAILED + runner.status = runner_info.RunnerStatus.FAILED await notify_project_changed(project) runner.initialized_event.set() raise RunnerFailedToStart(f"Runner failed to update config: {exception.message}") @@ -304,7 +346,10 @@ async def _init_runner( f"Updated config of runner {runner.working_dir_path}," f" process id {runner.process_id}" ) - project.status = domain.ProjectStatus.RUNNING + + +async def _finish_runner_init(runner: runner_info.ExtensionRunnerInfo, project: domain.Project, ws_context: context.WorkspaceContext) -> None: + runner.status = runner_info.RunnerStatus.RUNNING await notify_project_changed(project) await send_opened_files( @@ -314,6 +359,22 @@ async def _init_runner( runner.initialized_event.set() +def save_runners_from_tasks_in_context(tasks: list[asyncio.Task], ws_context: context.WorkspaceContext) -> None: + extension_runners: list[runner_info.ExtensionRunnerInfo] = [ + runner.result() for runner in tasks if runner is not None + ] + + for new_runner in extension_runners: + save_runner_in_context(runner=new_runner, ws_context=ws_context) + + +def save_runner_in_context(runner: runner_info.ExtensionRunnerInfo, ws_context: context.WorkspaceContext) -> None: + if runner.working_dir_path not in ws_context.ws_projects_extension_runners: + ws_context.ws_projects_extension_runners[runner.working_dir_path] = {} + ws_context.ws_projects_extension_runners[runner.working_dir_path][runner.env_name] = runner + + + async def send_opened_files( runner: runner_info.ExtensionRunnerInfo, opened_files: list[domain.TextDocumentInfo] ): diff --git a/src/finecode/workspace_manager/runner/runner_info.py b/src/finecode/workspace_manager/runner/runner_info.py index 80abf40..ec81c2a 100644 --- a/src/finecode/workspace_manager/runner/runner_info.py +++ b/src/finecode/workspace_manager/runner/runner_info.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import enum import logging import shlex from dataclasses import dataclass @@ -65,14 +66,25 @@ async def server_exit(self, server): @dataclass class ExtensionRunnerInfo: working_dir_path: Path + env_name: str + status: RunnerStatus # NOTE: initialized doesn't mean the runner is running, check its status initialized_event: asyncio.Event - client: CustomJsonRpcClient + # e.g. if there is no venv for env, client can be None + client: CustomJsonRpcClient | None = None keep_running_request_task: asyncio.Task | None = None @property def process_id(self) -> int: - if self.client._server is not None: + if self.client is not None and self.client._server is not None: return self.client._server.pid else: return 0 + + +class RunnerStatus(enum.Enum): + READY_TO_START = enum.auto() + NO_VENV = enum.auto() + FAILED = enum.auto() + RUNNING = enum.auto() + EXITED = enum.auto() diff --git a/src/finecode/workspace_manager/services.py b/src/finecode/workspace_manager/services.py index 1a69aea..134da54 100644 --- a/src/finecode/workspace_manager/services.py +++ b/src/finecode/workspace_manager/services.py @@ -2,6 +2,7 @@ import typing from loguru import logger +import ordered_set from finecode.workspace_manager import ( context, @@ -9,49 +10,62 @@ payload_preprocessor, user_messages, ) -from finecode.workspace_manager.runner import manager as runner_manager +from finecode.workspace_manager.runner import manager as runner_manager, runner_info from finecode.workspace_manager.runner import runner_client -async def restart_extension_runner( +async def restart_extension_runners( runner_working_dir_path: pathlib.Path, ws_context: context.WorkspaceContext ) -> None: # TODO: reload config? try: - runner = ws_context.ws_projects_extension_runners[runner_working_dir_path] + runners_by_env = ws_context.ws_projects_extension_runners[runner_working_dir_path] except KeyError: logger.error(f"Cannot find runner for {runner_working_dir_path}") return - await runner_manager.stop_extension_runner(runner) - del ws_context.ws_projects_extension_runners[runner_working_dir_path] - - new_runner = await runner_manager.start_extension_runner( - runner_dir=runner_working_dir_path, ws_context=ws_context - ) - if new_runner is None: - logger.error("Extension runner didn't start") - return - - ws_context.ws_projects_extension_runners[runner_working_dir_path] = new_runner - await runner_manager._init_runner( - new_runner, - ws_context.ws_projects[runner.working_dir_path], - ws_context, - ) + new_runners_by_env: dict[str, runner_info.ExtensionRunnerInfo] = {} + for runner in runners_by_env.values(): + await runner_manager.stop_extension_runner(runner) + + new_runner = await runner_manager.start_extension_runner( + runner_dir=runner_working_dir_path, env_name=runner.env_name, ws_context=ws_context + ) + if new_runner is None: + logger.error("Extension runner didn't start") + continue + new_runners_by_env[runner.env_name] = new_runner + + ws_context.ws_projects_extension_runners[runner_working_dir_path] = new_runners_by_env + + # parallel? + for runner in new_runners_by_env.values(): + await runner_manager._init_runner( + runner, + ws_context.ws_projects[runner.working_dir_path], + ws_context, + ) def on_shutdown(ws_context: context.WorkspaceContext): - running_runners = [ - runner - for runner in ws_context.ws_projects_extension_runners.values() - if ws_context.ws_projects[runner.working_dir_path].status - == domain.ProjectStatus.RUNNING - ] + + running_runners = [] + for runners_by_env in ws_context.ws_projects_extension_runners.values(): + for runner in runners_by_env.values(): + if runner.status == runner_info.RunnerStatus.RUNNING: + running_runners.append(runner) + logger.trace(f"Stop all {len(running_runners)} running extension runners") for runner in running_runners: runner_manager.stop_extension_runner_sync(runner=runner) + + # TODO: stop MCP if running + + +class ActionRunFailed(Exception): + def __init__(self, message: str) -> None: + self.message = message RunResultFormat = runner_client.RunResultFormat @@ -64,36 +78,90 @@ async def run_action( project_def: domain.Project, ws_context: context.WorkspaceContext, result_format: RunResultFormat = RunResultFormat.JSON, + preprocess_payload: bool = True ) -> RunActionResponse: formatted_params = str(params) if len(formatted_params) > 100: formatted_params = f"{formatted_params[:100]}..." logger.trace(f"Execute action {action_name} with {formatted_params}") - if project_def.status != domain.ProjectStatus.RUNNING: - logger.error( - f"Extension runner is not running in {project_def.dir_path}." - " Please check logs." + if project_def.status != domain.ProjectStatus.CONFIG_VALID: + raise ActionRunFailed(f"Project {project_def.dir_path} has no valid configuration and finecode." + " Please check logs.") + + if preprocess_payload: + payload = payload_preprocessor.preprocess_for_project( + action_name=action_name, payload=params, project_dir_path=project_def.dir_path, + ws_context=ws_context + ) + else: + payload = params + + # cases: + # - base: all action handlers are in one env + # -> send `run_action` request to runner in env and let it handle concurrency etc. + # It could be done also in workspace manager, but handlers share run context + # - mixed envs: action handlers are in different envs + # -- concurrent execution of handlers + # -- sequential execution of handlers + assert project_def.actions is not None + action = next(action for action in project_def.actions if action.name == action_name) + all_handlers_envs = ordered_set.OrderedSet([handler.env for handler in action.handlers]) + all_handlers_are_in_one_env = len(all_handlers_envs) == 1 + + if all_handlers_are_in_one_env: + env_name = all_handlers_envs[0] + response = await _run_action_in_env_runner( + action_name=action_name, + payload=payload, + env_name=env_name, + project_def=project_def, + ws_context=ws_context, + result_format=result_format ) - return RunActionResponse(result={}, return_code=1) + else: + # TODO: concurrent vs sequential, this value should be taken from action config + run_concurrently = False # action_name == 'lint' + if run_concurrently: + ... + raise NotImplementedError() + else: + for handler in action.handlers: + # TODO: manage run context + response = await _run_action_in_env_runner( + action_name=action_name, + payload=payload, + env_name=handler.env, + project_def=project_def, + ws_context=ws_context, + result_format=result_format + ) - payload = payload_preprocessor.preprocess_for_project( - action_name=action_name, payload=params, project_dir_path=project_def.dir_path - ) + return response - # extension runner is running for this project, send command to it + +async def _run_action_in_env_runner( + action_name: str, + payload: dict[str, typing.Any], + env_name: str, + project_def: domain.Project, + ws_context: context.WorkspaceContext, + result_format: RunResultFormat = RunResultFormat.JSON, +): + runners_by_env = ws_context.ws_projects_extension_runners[project_def.dir_path] + runner = runners_by_env[env_name] + if runner.status != runner_info.RunnerStatus.RUNNING: + raise ActionRunFailed(f"Runner {env_name} in project {project_def.dir_path} is not running. Status: {runner.status}") + try: response = await runner_client.run_action( - runner=ws_context.ws_projects_extension_runners[project_def.dir_path], + runner=runner, action_name=action_name, params=payload, options={"result_format": result_format}, ) except runner_client.BaseRunnerRequestException as error: await user_messages.error(f"Action {action_name} failed: {error.message}") - if result_format == runner_client.RunResultFormat.JSON: - return RunActionResponse(result={}, return_code=1) - else: - return RunActionResponse(result="", return_code=1) - + raise ActionRunFailed(f"Action {action_name} failed: {error.message}") + return response diff --git a/tests/__testdata__/list_ws/cli_tool/finecode.sh b/tests/__testdata__/list_ws/cli_tool/finecode.sh deleted file mode 100644 index 176acc2..0000000 --- a/tests/__testdata__/list_ws/cli_tool/finecode.sh +++ /dev/null @@ -1 +0,0 @@ -poetry run python \ No newline at end of file diff --git a/tests/__testdata__/list_ws/ui_app/finecode.sh b/tests/__testdata__/list_ws/ui_app/finecode.sh deleted file mode 100644 index 176acc2..0000000 --- a/tests/__testdata__/list_ws/ui_app/finecode.sh +++ /dev/null @@ -1 +0,0 @@ -poetry run python \ No newline at end of file diff --git a/tests/__testdata__/nested_package/pyback/finecode.sh b/tests/__testdata__/nested_package/pyback/finecode.sh deleted file mode 100644 index 176acc2..0000000 --- a/tests/__testdata__/nested_package/pyback/finecode.sh +++ /dev/null @@ -1 +0,0 @@ -poetry run python \ No newline at end of file From 92e8bc565484b27dd85056e10d883aa1947e1c15 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 23 Jun 2025 08:11:26 +0200 Subject: [PATCH 02/46] Finish proxy utils with envs --- src/finecode/workspace_manager/cli_app/run.py | 4 +-- .../lsp_server/endpoints/action_tree.py | 20 +++++++---- .../lsp_server/endpoints/diagnostics.py | 33 ++++++------------ src/finecode/workspace_manager/proxy_utils.py | 34 ++++++++++++------- 4 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/finecode/workspace_manager/cli_app/run.py b/src/finecode/workspace_manager/cli_app/run.py index e52c461..657d1bc 100644 --- a/src/finecode/workspace_manager/cli_app/run.py +++ b/src/finecode/workspace_manager/cli_app/run.py @@ -215,9 +215,9 @@ async def run_actions_in_running_project( ) run_tasks.append(run_task) except ExceptionGroup as eg: - for error in eg: + for error in eg.exceptions: logger.exception(error) - raise RunFailed(f"Running of action {action_name} failed") + raise RunFailed(f"Running of actions {actions} failed") for idx, run_task in enumerate(run_tasks): run_result = run_task.result() diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py b/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py index cb50294..b3b3784 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py +++ b/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py @@ -317,13 +317,19 @@ async def run_action( action_name = splitted_action_id[1] - response = await wm_services.run_action( - action_name=action_name, - params=request.params, - project_def=project_def, - ws_context=global_state.ws_context, - ) - return schemas.RunActionResponse(result=response.result) + try: + response = await wm_services.run_action( + action_name=action_name, + params=request.params, + project_def=project_def, + ws_context=global_state.ws_context, + ) + result = response.result + except wm_services.ActionRunFailed as exception: + logger.error(exception.message) + result = {} + + return schemas.RunActionResponse(result=result) async def notify_changed_action_node( diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py b/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py index 2f86de6..3dad26b 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py +++ b/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py @@ -8,19 +8,15 @@ from loguru import logger from lsprotocol import types -import ordered_set from finecode import pygls_types_utils -from finecode.workspace_manager import domain, project_analyzer, proxy_utils +from finecode.workspace_manager import domain, project_analyzer, proxy_utils, services, context from finecode.workspace_manager.lsp_server import global_state -from finecode.workspace_manager.runner import runner_client from finecode_extension_api.actions import lint as lint_action if TYPE_CHECKING: from pygls.lsp.server import LanguageServer - from finecode.workspace_manager.runner import runner_info - def map_lint_message_to_diagnostic( lint_message: lint_action.LintMessage, @@ -224,7 +220,7 @@ async def document_diagnostic( @dataclass class LintActionExecInfo: - runner: runner_info.ExtensionRunnerInfo + project_dir_path: Path action_name: str request_data: dict[str, str | list[str]] = field(default_factory=dict) @@ -239,7 +235,8 @@ async def run_workspace_diagnostic_with_partial_results( action_name="lint", params=exec_info.request_data, partial_result_token=partial_result_token, - runner=exec_info.runner, + project_dir_path=exec_info.project_dir_path, + ws_context=global_state.ws_context ) as response: async for partial_response in response: lint_subresult = lint_action.LintRunResult(**partial_response) @@ -284,17 +281,14 @@ async def workspace_diagnostic_with_partial_results( return types.WorkspaceDiagnosticReport(items=[]) -async def workspace_diagnostic_with_full_result(exec_infos: list[LintActionExecInfo]): +async def workspace_diagnostic_with_full_result(exec_infos: list[LintActionExecInfo], ws_context: context.WorkspaceContext): send_tasks: list[asyncio.Task] = [] try: async with asyncio.TaskGroup() as tg: for exec_info in exec_infos: + project = ws_context.ws_projects[exec_info.project_dir_path] task = tg.create_task( - runner_client.run_action( - runner=exec_info.runner, - action_name=exec_info.action_name, - params=exec_info.request_data, - ) + services.run_action(action_name=exec_info.action_name, params=exec_info.request_data, project_def=project, ws_context=ws_context, preprocess_payload=False) ) send_tasks.append(task) except ExceptionGroup as eg: @@ -334,14 +328,9 @@ async def _workspace_diagnostic( for project_dir_path in relevant_projects_paths: project = global_state.ws_context.ws_projects[project_dir_path] - action = next(action for action in project.actions if action.name == 'lint') - action_envs = ordered_set.OrderedSet([handler.env for handler in action.handlers]) - runners_by_env = global_state.ws_context.ws_projects_extension_runners[project_dir_path] - for env in action_envs: - runner = runners_by_env[env] - exec_info_by_project_dir_path[project_dir_path] = LintActionExecInfo( - runner=runner, action_name="lint" - ) + exec_info_by_project_dir_path[project_dir_path] = LintActionExecInfo( + project_dir_path=project_dir_path, action_name="lint" + ) # find which runner is responsible for which files # currently FineCode supports only raw python files, find them in each ws project @@ -379,7 +368,7 @@ async def _workspace_diagnostic( exec_infos=exec_infos, partial_result_token=params.partial_result_token ) else: - return await workspace_diagnostic_with_full_result(exec_infos=exec_infos) + return await workspace_diagnostic_with_full_result(exec_infos=exec_infos, ws_context=global_state.ws_context) async def workspace_diagnostic( diff --git a/src/finecode/workspace_manager/proxy_utils.py b/src/finecode/workspace_manager/proxy_utils.py index 339ce68..6f7e2c6 100644 --- a/src/finecode/workspace_manager/proxy_utils.py +++ b/src/finecode/workspace_manager/proxy_utils.py @@ -5,6 +5,7 @@ from typing import Any from loguru import logger +import ordered_set from finecode.workspace_manager import context, domain, find_project, services from finecode.workspace_manager.services import ActionRunFailed @@ -159,11 +160,12 @@ async def run_with_partial_results( action_name: str, params: dict[str, Any], partial_result_token: int | str, - runner: runner_info.ExtensionRunnerInfo, + project_dir_path: pathlib.Path, + ws_context: context.WorkspaceContext ) -> collections.abc.AsyncIterator[ collections.abc.AsyncIterable[domain.PartialResultRawValue] ]: - logger.trace(f"Run {action_name} in runner {runner.working_dir_path}") + logger.trace(f"Run {action_name} in project {project_dir_path}") result: AsyncList[domain.PartialResultRawValue] = AsyncList() try: @@ -173,16 +175,22 @@ async def run_with_partial_results( result_list=result, partial_result_token=partial_result_token ) ) - tg.create_task( - run_action_and_notify( - action_name=action_name, - params=params, - partial_result_token=partial_result_token, - runner=runner, - result_list=result, - partial_results_task=partial_results_task, + project = ws_context.ws_projects[project_dir_path] + action = next(action for action in project.actions if action.name == 'lint') + action_envs = ordered_set.OrderedSet([handler.env for handler in action.handlers]) + runners_by_env = ws_context.ws_projects_extension_runners[project_dir_path] + for env in action_envs: + runner = runners_by_env[env] + tg.create_task( + run_action_and_notify( + action_name=action_name, + params=params, + partial_result_token=partial_result_token, + runner=runner, + result_list=result, + partial_results_task=partial_results_task, + ) ) - ) yield result except ExceptionGroup as eg: @@ -203,12 +211,12 @@ async def find_action_project_and_run_with_partial_results( project_path = find_action_project( file_path=file_path, action_name=action_name, ws_context=ws_context ) - # TODO return run_with_partial_results( action_name=action_name, params=params, partial_result_token=partial_result_token, - runner=runner, + project_dir_path=project_path, + ws_context=ws_context ) From ba21ba0661ab20bda5a25d872991cc19aa87c1e3 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Tue, 12 Aug 2025 12:52:51 +0200 Subject: [PATCH 03/46] Prepare envs CLI, action and handlers. Dump config as action. New interfaces: iprojectinfoprover, iactionrunner. --- .github/workflows/ci-cd.yml | 143 +---- extensions/fine_python_pip/.gitignore | 2 + extensions/fine_python_pip/README.md | 1 + extensions/fine_python_pip/pyproject.toml | 21 + .../src/fine_python_pip/__init__.py | 5 + .../fine_python_pip/prepare_env_handler.py | 47 ++ extensions/fine_python_pip/tests/__init__.py | 0 extensions/fine_python_virtualenv/.gitignore | 4 + extensions/fine_python_virtualenv/README.md | 0 .../fine_python_virtualenv/pyproject.toml | 24 + .../src/fine_python_virtualenv/__init__.py | 6 + .../src/fine_python_virtualenv/handler.py | 39 ++ .../fine_python_virtualenv/tests/__init__.py | 0 .../finecode_dev_common_preset/preset.toml | 6 + .../actions/dump_config.py | 54 ++ .../actions/prepare_envs.py | 62 ++ .../interfaces/iactionrunner.py | 24 + .../interfaces/iprojectinfoprovider.py | 5 + finecode_extension_api/pyproject.toml | 18 +- .../extension_runner/_services/__init__.py | 0 .../extension_runner/_services/run_action.py | 538 ++++++++++++++++++ .../action_handlers/__init__.py | 9 + .../action_handlers/dump_config.py | 26 + .../action_handlers/dump_config_save.py | 32 ++ .../prepare_envs_dump_configs.py | 76 +++ src/finecode/extension_runner/bootstrap.py | 90 --- src/finecode/extension_runner/di/__init__.py | 0 src/finecode/extension_runner/di/_state.py | 5 + src/finecode/extension_runner/di/bootstrap.py | 92 +++ src/finecode/extension_runner/di/resolver.py | 23 + .../extension_runner/impls/action_runner.py | 17 + .../impls/project_info_provider.py | 19 + src/finecode/extension_runner/lsp_server.py | 16 +- src/finecode/extension_runner/services.py | 528 +---------------- src/finecode/extension_runner/start.py | 5 +- src/finecode/workspace_manager/cli.py | 33 ++ .../workspace_manager/cli_app/dump_config.py | 15 +- .../workspace_manager/cli_app/prepare_envs.py | 104 ++++ src/finecode/workspace_manager/cli_app/run.py | 73 ++- .../workspace_manager/config/read_configs.py | 52 +- .../workspace_manager/payload_preprocessor.py | 26 +- .../workspace_manager/runner/manager.py | 76 ++- 42 files changed, 1519 insertions(+), 797 deletions(-) create mode 100644 extensions/fine_python_pip/.gitignore create mode 100644 extensions/fine_python_pip/README.md create mode 100644 extensions/fine_python_pip/pyproject.toml create mode 100644 extensions/fine_python_pip/src/fine_python_pip/__init__.py create mode 100644 extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py create mode 100644 extensions/fine_python_pip/tests/__init__.py create mode 100644 extensions/fine_python_virtualenv/.gitignore create mode 100644 extensions/fine_python_virtualenv/README.md create mode 100644 extensions/fine_python_virtualenv/pyproject.toml create mode 100644 extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py create mode 100644 extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py create mode 100644 extensions/fine_python_virtualenv/tests/__init__.py create mode 100644 finecode_extension_api/finecode_extension_api/actions/dump_config.py create mode 100644 finecode_extension_api/finecode_extension_api/actions/prepare_envs.py create mode 100644 finecode_extension_api/finecode_extension_api/interfaces/iactionrunner.py create mode 100644 finecode_extension_api/finecode_extension_api/interfaces/iprojectinfoprovider.py create mode 100644 src/finecode/extension_runner/_services/__init__.py create mode 100644 src/finecode/extension_runner/_services/run_action.py create mode 100644 src/finecode/extension_runner/action_handlers/__init__.py create mode 100644 src/finecode/extension_runner/action_handlers/dump_config.py create mode 100644 src/finecode/extension_runner/action_handlers/dump_config_save.py create mode 100644 src/finecode/extension_runner/action_handlers/prepare_envs_dump_configs.py delete mode 100644 src/finecode/extension_runner/bootstrap.py create mode 100644 src/finecode/extension_runner/di/__init__.py create mode 100644 src/finecode/extension_runner/di/_state.py create mode 100644 src/finecode/extension_runner/di/bootstrap.py create mode 100644 src/finecode/extension_runner/di/resolver.py create mode 100644 src/finecode/extension_runner/impls/action_runner.py create mode 100644 src/finecode/extension_runner/impls/project_info_provider.py create mode 100644 src/finecode/workspace_manager/cli_app/prepare_envs.py diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 3db7370..ea7d06a 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -9,6 +9,11 @@ defaults: run: shell: bash +env: + # python version for dev workspace + DEV_WORKSPACE_PYTHON_VERSION: '3.13' + + jobs: build: runs-on: ${{ matrix.os }} @@ -17,7 +22,6 @@ jobs: max-parallel: 1 matrix: os: [ubuntu-24.04, macos-13, windows-2022] - python-version: ["3.13"] include: - os: ubuntu-24.04 name: Linux @@ -29,150 +33,39 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install poetry - run: pipx install poetry==2.1.2 - - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python ${{ env.DEV_WORKSPACE_PYTHON_VERSION }} uses: actions/setup-python@v5 with: - python-version: ${{ matrix.python-version }} - cache: "poetry" + python-version: ${{ env.DEV_WORKSPACE_PYTHON_VERSION }} - name: Install dependencies run: | - # poetry default git client `dulwich` doesn't work properly with git urls - # to dependencies, which we use - poetry config system-git-client true - - FINECODE_SH_TEMPLATE="poetry run python" - - poetry install - echo $FINECODE_SH_TEMPLATE > finecode.sh - - pushd tests/list_ws/backend - poetry install - echo $FINECODE_SH_TEMPLATE > finecode.sh - popd - - pushd tests/list_ws/cli_tool - poetry install - echo $FINECODE_SH_TEMPLATE > finecode.sh - popd - - pushd tests/list_ws/ui_app - poetry install - echo $FINECODE_SH_TEMPLATE > finecode.sh - popd - - pushd tests/nested_package/pyback - poetry install - echo $FINECODE_SH_TEMPLATE > finecode.sh - popd + python -m venv .venvs/dev_workspace + source .venvs/dev_workspace/bin/activate + python -m pip install --upgrade pip==25.1.1 + python -m pip install --group="dev_workspace" - pushd finecode_extension_api - poetry install - echo $FINECODE_SH_TEMPLATE > finecode.sh - popd + python -m finecode prepare_envs shell: bash + # TODO: install all other supported python versions. Version can be extracted from finecode + # - name: Lint # run: | # poetry run python -m finecode run lint # shell: bash - - name: Build finecode_extension_api - if: runner.os == 'Linux' - run: | - pushd finecode_extension_api - poetry build - popd - shell: bash - - - name: Build fine_python_ast - if: runner.os == 'Linux' - run: | - pushd extensions/fine_python_ast - poetry build - popd - shell: bash - - - name: Build fine_python_black - if: runner.os == 'Linux' - run: | - pushd extensions/fine_python_black - poetry build - popd - shell: bash - - - name: Build fine_python_flake8 - if: runner.os == 'Linux' - run: | - pushd extensions/fine_python_flake8 - poetry build - popd - shell: bash - - - name: Build fine_python_isort + - name: Build all packages if: runner.os == 'Linux' run: | - pushd extensions/fine_python_isort - poetry build - popd - shell: bash - - - name: Build fine_python_module_exports - if: runner.os == 'Linux' - run: | - pushd extensions/fine_python_module_exports - poetry build - popd - shell: bash - - - name: Build fine_python_mypy - if: runner.os == 'Linux' - run: | - pushd extensions/fine_python_mypy - poetry build - popd - shell: bash - - - name: Build fine_python_format - if: runner.os == 'Linux' - run: | - pushd presets/fine_python_format - poetry build - popd - shell: bash - - - name: Build fine_python_lint - if: runner.os == 'Linux' - run: | - pushd presets/fine_python_lint - poetry build - popd - shell: bash - - - name: Build fine_python_recommended - if: runner.os == 'Linux' - run: | - pushd presets/fine_python_recommended - poetry build - popd - shell: bash - - - name: Build finecode - if: runner.os == 'Linux' - run: | - python -m venv .dev_workspace_venv - source .dev_workspace_venv/bin/activate - python -m pip install --upgrade pip==25.1.1 - python -m pip install --group="dev_workspace" - python -m build + source .venvs/dev_workspace/bin/activate + python -m finecode run build shell: bash - name: Collect all distribution packages if: runner.os == 'Linux' run: | + # TODO: finecode action to copy only updated packages in dist mkdir -p dist cp finecode_extension_api/dist/* dist/ cp extensions/fine_python_ast/dist/* dist/ diff --git a/extensions/fine_python_pip/.gitignore b/extensions/fine_python_pip/.gitignore new file mode 100644 index 0000000..42b6063 --- /dev/null +++ b/extensions/fine_python_pip/.gitignore @@ -0,0 +1,2 @@ +.venvs +*.egg-info \ No newline at end of file diff --git a/extensions/fine_python_pip/README.md b/extensions/fine_python_pip/README.md new file mode 100644 index 0000000..d6ddb2d --- /dev/null +++ b/extensions/fine_python_pip/README.md @@ -0,0 +1 @@ +Run pip only as subprocess, not access its API programatically: https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program diff --git a/extensions/fine_python_pip/pyproject.toml b/extensions/fine_python_pip/pyproject.toml new file mode 100644 index 0000000..8f22ddc --- /dev/null +++ b/extensions/fine_python_pip/pyproject.toml @@ -0,0 +1,21 @@ +[project] +name = "fine-python-pip" +version = "0.1.0" +description = "" +authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] +readme = "README.md" +requires-python = ">=3.11, < 3.14" +dependencies = ["finecode_extension_api==0.1.0"] + +[dependency-groups] +dev_workspace = ["finecode==0.2.0"] +dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] + +[tool.finecode] +presets = [{ source = "finecode_dev_common_preset" }] + +[tool.finecode.env.runtime.dependencies] +finecode_extension_api = { path = "../../finecode_extension_api", editable = true } + +[tool.finecode.env.dev_no_runtime.dependencies] +finecode_dev_common_preset = { path = "../../finecode_dev_common_preset", editable = true } diff --git a/extensions/fine_python_pip/src/fine_python_pip/__init__.py b/extensions/fine_python_pip/src/fine_python_pip/__init__.py new file mode 100644 index 0000000..244480c --- /dev/null +++ b/extensions/fine_python_pip/src/fine_python_pip/__init__.py @@ -0,0 +1,5 @@ +from .prepare_env_handler import PipPrepareEnvHandler + +__all__ = [ + 'PipPrepareEnvHandler' +] \ No newline at end of file diff --git a/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py b/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py new file mode 100644 index 0000000..7b9b1a9 --- /dev/null +++ b/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py @@ -0,0 +1,47 @@ +import asyncio +import dataclasses + +from finecode_extension_api import code_action +from finecode_extension_api.actions import prepare_envs as prepare_envs_action +from finecode_extension_api.interfaces import (icommandrunner, ilogger) + + +@dataclasses.dataclass +class PipPrepareEnvHandlerConfig(code_action.ActionHandlerConfig): + ... + + +class PipPrepareEnvHandler( + code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, PipPrepareEnvHandlerConfig] +): + def __init__(self, command_runner: icommandrunner.ICommandRunner, logger: ilogger.ILogger) -> None: + self.command_runner = command_runner + self.logger = logger + + async def run( + self, + payload: prepare_envs_action.PrepareEnvsRunPayload, + run_context: prepare_envs_action.PrepareEnvsRunContext, + ) -> prepare_envs_action.PrepareEnvsRunResult: + install_processes: list[icommandrunner.IAsyncProcess] = [] + for env_info in payload.envs: + python_executable = env_info.venv_dir_path / 'bin' / 'python' + project_def_path = run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] + pip_params = '' + if env_info.name == 'runtime': + pip_params += ' -e .' + + process = await self.command_runner.run(f'{python_executable} -m pip --disable-pip-version-check install {pip_params} --group="{env_info.name}"', cwd=project_def_path.parent) + install_processes.append(process) + + async with asyncio.TaskGroup() as tg: + for process in install_processes: + tg.create_task(process.wait_for_end()) + + for idx, process in enumerate(install_processes): + if process.get_exit_code() != 0: + env_info = payload.envs[idx] + project_def_path = run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] + self.logger.error(f'Installation of dependencies in env {env_info.name} from {project_def_path} failed:\nstdout: {process.get_output()}\nstderr: {process.get_error_output()}') + # TODO: return code, return logs? + return prepare_envs_action.PrepareEnvsRunResult(results=[env_info.venv_dir_path for env_info in payload.envs]) diff --git a/extensions/fine_python_pip/tests/__init__.py b/extensions/fine_python_pip/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/extensions/fine_python_virtualenv/.gitignore b/extensions/fine_python_virtualenv/.gitignore new file mode 100644 index 0000000..5a23f9f --- /dev/null +++ b/extensions/fine_python_virtualenv/.gitignore @@ -0,0 +1,4 @@ +.venvs +build/ +src/*.egg-info/ +__pycache__ \ No newline at end of file diff --git a/extensions/fine_python_virtualenv/README.md b/extensions/fine_python_virtualenv/README.md new file mode 100644 index 0000000..e69de29 diff --git a/extensions/fine_python_virtualenv/pyproject.toml b/extensions/fine_python_virtualenv/pyproject.toml new file mode 100644 index 0000000..08f8cbb --- /dev/null +++ b/extensions/fine_python_virtualenv/pyproject.toml @@ -0,0 +1,24 @@ +[project] +name = "fine-python-virtualenv" +version = "0.1.0" +description = "" +authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] +readme = "README.md" +requires-python = ">=3.11, < 3.14" +dependencies = [ + "finecode_extension_api==0.1.0", + "virtualenv (>=20.0.0,<21.0.0)", +] + +[dependency-groups] +dev_workspace = ["finecode==0.2.0"] +dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] + +[tool.finecode] +presets = [{ source = "finecode_dev_common_preset" }] + +[tool.finecode.env.runtime.dependencies] +finecode_extension_api = { path = "../../finecode_extension_api", editable = true } + +[tool.finecode.env.dev_no_runtime.dependencies] +finecode_dev_common_preset = { path = "../../finecode_dev_common_preset", editable = true } diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py new file mode 100644 index 0000000..1aa1091 --- /dev/null +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py @@ -0,0 +1,6 @@ +from .handler import VirtualenvPrepareEnvHandler + + +__all__ = [ + 'VirtualenvPrepareEnvHandler' +] diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py new file mode 100644 index 0000000..767dd10 --- /dev/null +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py @@ -0,0 +1,39 @@ +from finecode_extension_api.interfaces import ilogger +import virtualenv + +from finecode_extension_api import code_action +from finecode_extension_api.actions import prepare_envs as prepare_envs_action + + +class VirtualenvPrepareEnvHandlerConfig(code_action.ActionHandlerConfig): + ... + + +class VirtualenvPrepareEnvHandler( + code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, VirtualenvPrepareEnvHandlerConfig] +): + def __init__( + self, + config: VirtualenvPrepareEnvHandlerConfig, + logger: ilogger.ILogger, + ) -> None: + self.config = config + self.logger = logger + + async def run( + self, + payload: prepare_envs_action.PrepareEnvsRunPayload, + run_context: prepare_envs_action.PrepareEnvsRunContext, + ) -> prepare_envs_action.PrepareEnvsRunResult: + # create virtual envs + + # would it be faster parallel? + for env_info in payload.envs: + self.logger.info(f"Creating virtualenv {env_info.venv_dir_path}") + if not env_info.venv_dir_path.exists(): + # TODO: '-p ' + virtualenv.cli_run([env_info.venv_dir_path.as_posix()], options=None, setup_logging=False, env=None) + else: + self.logger.info(f"Virtualenv in {env_info} exists already") + + return prepare_envs_action.PrepareEnvsRunResult(results=[env_info.venv_dir_path for env_info in payload.envs]) diff --git a/extensions/fine_python_virtualenv/tests/__init__.py b/extensions/fine_python_virtualenv/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/finecode_dev_common_preset/src/finecode_dev_common_preset/preset.toml b/finecode_dev_common_preset/src/finecode_dev_common_preset/preset.toml index 545d1a0..1c79174 100644 --- a/finecode_dev_common_preset/src/finecode_dev_common_preset/preset.toml +++ b/finecode_dev_common_preset/src/finecode_dev_common_preset/preset.toml @@ -6,3 +6,9 @@ presets = [ [tool.finecode.action_handler.black.config] preview = true + +# currently, all packages in finecode repository are pure python packages, reuse +# setuptools build in all of them +[build-system] +requires = ["setuptools>=64", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" diff --git a/finecode_extension_api/finecode_extension_api/actions/dump_config.py b/finecode_extension_api/finecode_extension_api/actions/dump_config.py new file mode 100644 index 0000000..b150e77 --- /dev/null +++ b/finecode_extension_api/finecode_extension_api/actions/dump_config.py @@ -0,0 +1,54 @@ +import dataclasses +import pathlib +import sys +import typing + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +from finecode_extension_api import code_action, textstyler + + +@dataclasses.dataclass +class DumpConfigRunPayload(code_action.RunActionPayload): + # `source_file_path` is not for reading, config is already read and its content is + # in `project_raw_config`, but for providing config path to allow for example to + # resolve relative pathes in project config + source_file_path: pathlib.Path + project_raw_config: dict[str, typing.Any] + target_file_path: pathlib.Path + + +class DumpConfigRunContext(code_action.RunActionContext): + def __init__( + self, + run_id: int, + ) -> None: + super().__init__(run_id=run_id) + + self.raw_config_dump: dict[str, typing.Any] = {} + + async def init(self, initial_payload: DumpConfigRunPayload) -> None: + self.raw_config_dump = initial_payload.project_raw_config + + +@dataclasses.dataclass +class DumpConfigRunResult(code_action.RunActionResult): + # TODO: return dumped config as result, not only save in file + ... + + @override + def update(self, other: code_action.RunActionResult) -> None: + if not isinstance(other, DumpConfigRunResult): + return + + def to_text(self) -> str | textstyler.StyledText: + return '' + + +class DumpConfigAction(code_action.Action): + PAYLOAD_TYPE = DumpConfigRunPayload + RUN_CONTEXT_TYPE = DumpConfigRunContext + RESULT_TYPE = DumpConfigRunResult diff --git a/finecode_extension_api/finecode_extension_api/actions/prepare_envs.py b/finecode_extension_api/finecode_extension_api/actions/prepare_envs.py new file mode 100644 index 0000000..d52708f --- /dev/null +++ b/finecode_extension_api/finecode_extension_api/actions/prepare_envs.py @@ -0,0 +1,62 @@ +import dataclasses +import pathlib +import sys + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +from finecode_extension_api import code_action, textstyler + + +@dataclasses.dataclass +class EnvInfo: + name: str + venv_dir_path: pathlib.Path + project_def_path: pathlib.Path + + +@dataclasses.dataclass +class PrepareEnvsRunPayload(code_action.RunActionPayload): + envs: list[EnvInfo] + + +class PrepareEnvsRunContext(code_action.RunActionContext): + def __init__( + self, + run_id: int, + ) -> None: + super().__init__(run_id=run_id) + + # project def pathes are stored also in context, because prepare envs can run + # tools like pip which expected 'normalized' project definition(=without + # additional features which finecode provides). So the usual workflow looks like + # normalizing(dumping) configuration first and then use dumped config for + # further handlers. + self.project_def_path_by_venv_dir_path: dict[pathlib.Path, pathlib.Path] = {} + + async def init(self, initial_payload: PrepareEnvsRunPayload) -> None: + for env_info in initial_payload.envs: + self.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = env_info.project_def_path + + +@dataclasses.dataclass +class PrepareEnvsRunResult(code_action.RunActionResult): + # TODO: statuses, errors, logs? + # TODO: return code property + results: list[pathlib.Path] + + @override + def update(self, other: code_action.RunActionResult) -> None: + if not isinstance(other, PrepareEnvsRunResult): + return + + def to_text(self) -> str | textstyler.StyledText: + return '' + + +class PrepareEnvsAction(code_action.Action): + PAYLOAD_TYPE = PrepareEnvsRunPayload + RUN_CONTEXT_TYPE = PrepareEnvsRunContext + RESULT_TYPE = PrepareEnvsRunResult diff --git a/finecode_extension_api/finecode_extension_api/interfaces/iactionrunner.py b/finecode_extension_api/finecode_extension_api/interfaces/iactionrunner.py new file mode 100644 index 0000000..afa5478 --- /dev/null +++ b/finecode_extension_api/finecode_extension_api/interfaces/iactionrunner.py @@ -0,0 +1,24 @@ +from pathlib import Path +from typing import Any, Protocol + + +class IActionRunner(Protocol): + async def run_action( + self, name: str, payload: dict[str, Any] + ) -> dict[str, Any]: ... + + +class BaseRunActionException(Exception): + ... + + +class ActionNotFound(BaseRunActionException): + ... + + +class InvalidActionRunPayload(BaseRunActionException): + ... + + +class ActionRunFailed(BaseRunActionException): + pass diff --git a/finecode_extension_api/finecode_extension_api/interfaces/iprojectinfoprovider.py b/finecode_extension_api/finecode_extension_api/interfaces/iprojectinfoprovider.py new file mode 100644 index 0000000..32763b9 --- /dev/null +++ b/finecode_extension_api/finecode_extension_api/interfaces/iprojectinfoprovider.py @@ -0,0 +1,5 @@ +from typing import Any, Protocol + + +class IProjectInfoProvider(Protocol): + async def get_project_raw_config(self) -> dict[str, Any]: ... diff --git a/finecode_extension_api/pyproject.toml b/finecode_extension_api/pyproject.toml index 47f79bc..a80c3c3 100644 --- a/finecode_extension_api/pyproject.toml +++ b/finecode_extension_api/pyproject.toml @@ -5,18 +5,14 @@ description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = [ - "pydantic (>=2.10.6,<3.0.0)", - "typing-extensions (>=4.12.2,<5.0.0)", -] +dependencies = ["typing-extensions (>=4.12.2,<5.0.0)"] -[build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" - -[tool.poetry.group.dev.dependencies] -finecode = { git = "https://github.com/finecode-dev/finecode.git" } -finecode_dev_common_preset = { path = "../finecode_dev_common_preset", develop = true } +[dependency-groups] +dev_workspace = ["finecode==0.2.*"] +dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] [tool.finecode] presets = [{ source = "finecode_dev_common_preset" }] + +[tool.finecode.env.dev_no_runtime.dependencies] +finecode_dev_common_preset = { path = "../finecode_dev_common_preset", editable = true } diff --git a/src/finecode/extension_runner/_services/__init__.py b/src/finecode/extension_runner/_services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/finecode/extension_runner/_services/run_action.py b/src/finecode/extension_runner/_services/run_action.py new file mode 100644 index 0000000..d97f925 --- /dev/null +++ b/src/finecode/extension_runner/_services/run_action.py @@ -0,0 +1,538 @@ +import asyncio +import collections.abc +import dataclasses +import time +import inspect +import typing + +from loguru import logger +from pydantic.dataclasses import dataclass as pydantic_dataclass + +from finecode.extension_runner import project_dirs, run_utils, schemas, global_state, domain, context +from finecode_extension_api import code_action, textstyler +from finecode.extension_runner import ( + partial_result_sender as partial_result_sender_module, +) +from finecode.extension_runner.di import resolver as di_resolver + + +last_run_id: int = 0 +partial_result_sender: partial_result_sender_module.PartialResultSender + + +class ActionFailedException(Exception): ... + + +def set_partial_result_sender(send_func: typing.Callable) -> None: + global partial_result_sender + partial_result_sender = partial_result_sender_module.PartialResultSender( + sender=send_func, wait_time_ms=300 + ) + + +async def run_action( + request: schemas.RunActionRequest, options: schemas.RunActionOptions +) -> schemas.RunActionResponse: + global last_run_id + run_id = last_run_id + last_run_id += 1 + logger.trace(f"Run action '{request.action_name}', run id: {run_id}") + # TODO: check whether config is set: this will be solved by passing initial + # configuration as payload of initialize + if global_state.runner_context is None: + raise ActionFailedException( + "Run of action failed because extension runner is not initialized yet" + ) + + start_time = time.time_ns() + project_def = global_state.runner_context.project + + try: + action = project_def.actions[request.action_name] + except KeyError: + logger.error(f"R{run_id} | Action {request.action_name} not found") + raise ActionFailedException( + f"R{run_id} | Action {request.action_name} not found" + ) + + # design decisions: + # - keep payload unchanged between all subaction runs. + # For intermediate data use run_context + # - result is modifiable. Result of each subaction updates the previous result. + # In case of failure of subaction, at least result of all previous handlers is + # returned. (experimental) + # - execution of handlers can be concurrent or sequential. But executions of handler + # on iterable payloads(single parts) are always concurrent. + + try: + action_exec_info = global_state.runner_context.action_exec_info_by_name[ + request.action_name + ] + except KeyError: + action_exec_info = create_action_exec_info(action) + global_state.runner_context.action_exec_info_by_name[request.action_name] = ( + action_exec_info + ) + + # TODO: catch validation errors + payload: code_action.RunActionPayload | None = None + if action_exec_info.payload_type is not None: + payload_type_with_validation = pydantic_dataclass(action_exec_info.payload_type) + payload = payload_type_with_validation(**request.params) + + run_context: code_action.RunActionContext | None = None + if action_exec_info.run_context_type is not None: + constructor_args = resolve_func_args_with_di( + action_exec_info.run_context_type.__init__, + known_args={"run_id": lambda _: run_id}, + params_to_ignore=["self"], + ) + + run_context = action_exec_info.run_context_type(**constructor_args) + # TODO: handler errors + await run_context.init(initial_payload=payload) + + action_result: code_action.RunActionResult | None = None + runner_context = global_state.runner_context + + # instantiate only on demand? + project_path = project_def.path + project_cache_dir = project_dirs.get_project_dir(project_path=project_path) + action_context = code_action.ActionContext( + project_dir=project_path, cache_dir=project_cache_dir + ) + + # TODO: take value from action config + execute_handlers_concurrently = action.name == "lint" + partial_result_token = options.partial_result_token + send_partial_results = partial_result_token is not None + with action_exec_info.process_executor.activate(): + # action payload can be iterable or not + if isinstance(payload, collections.abc.AsyncIterable): + # iterable: `run` method should not calculate results itself, but call + # `partial_result_scheduler.schedule`. Then we execute provided + # coroutines either concurrently or sequentially. + logger.trace( + f"R{run_id} | Iterable payload, execute all handlers to schedule coros" + ) + for handler in action.handlers: + await execute_action_handler( + handler=handler, + payload=payload, + run_context=run_context, + run_id=run_id, + action_context=action_context, + action_exec_info=action_exec_info, + runner_context=runner_context, + ) + + parts = [part async for part in payload] + subresults_tasks: list[asyncio.Task] = [] + logger.trace( + "R{run_id} | Run subresult coros {exec_type} {partials} partial results".format( + run_id=run_id, + exec_type=( + "concurrently" + if execute_handlers_concurrently + else "sequentially" + ), + partials="with" if send_partial_results else "without", + ) + ) + try: + async with asyncio.TaskGroup() as tg: + for part in parts: + part_coros = ( + run_context.partial_result_scheduler.coroutines_by_key[part] + ) + del run_context.partial_result_scheduler.coroutines_by_key[part] + if execute_handlers_concurrently: + coro = run_subresult_coros_concurrently( + part_coros, + send_partial_results, + partial_result_token, + partial_result_sender, + action.name, + run_id, + ) + else: + coro = run_subresult_coros_sequentially( + part_coros, + send_partial_results, + partial_result_token, + partial_result_sender, + action.name, + run_id, + ) + subresult_task = tg.create_task(coro) + subresults_tasks.append(subresult_task) + except ExceptionGroup as eg: + logger.error(eg) + for exc in eg.exceptions: + logger.exception(exc) + raise ActionFailedException( + f"Running action handlers of '{action.name}' failed(Run {run_id})." + " See ER logs for more details" + ) + + if send_partial_results: + # all subresults are ready + logger.trace(f"R{run_id} | all subresults are ready, send them") + await partial_result_sender.send_all_immediately() + + for subresult_task in subresults_tasks: + result = subresult_task.result() + if result is not None: + if action_result is None: + action_result = result + else: + action_result.update(result) + else: + # action payload not iterable, just execute handlers on the whole payload + if execute_handlers_concurrently: + handlers_tasks: list[asyncio.Task] = [] + try: + async with asyncio.TaskGroup() as tg: + for handler in action.handlers: + handler_task = tg.create_task( + execute_action_handler( + handler=handler, + payload=payload, + run_context=run_context, + run_id=run_id, + action_context=action_context, + action_exec_info=action_exec_info, + runner_context=runner_context, + ) + ) + handlers_tasks.append(handler_task) + except ExceptionGroup as eg: + logger.error(eg) + for exc in eg.exceptions: + logger.exception(exc) + raise ActionFailedException( + f"Running action handlers of '{action.name}' failed" + f"(Run {run_id}). See ER logs for more details" + ) + + for handler_task in handlers_tasks: + coro_result = handler_task.result() + if coro_result is not None: + if action_result is None: + action_result = coro_result + else: + action_result.update(coro_result) + else: + for handler in action.handlers: + handler_result = await execute_action_handler( + handler=handler, + payload=payload, + run_context=run_context, + run_id=run_id, + action_context=action_context, + action_exec_info=action_exec_info, + runner_context=runner_context, + ) + + if handler_result is not None: + if action_result is None: + action_result = handler_result + else: + action_result.update(handler_result) + + serialized_result: dict[str, typing.Any] | str | None = None + result_format = "string" + run_return_code = code_action.RunReturnCode.SUCCESS + if isinstance(action_result, code_action.RunActionResult): + run_return_code = action_result.return_code + if options.result_format == "json": + serialized_result = dataclasses.asdict(action_result) + result_format = "json" + elif options.result_format == "string": + result_text = action_result.to_text() + if isinstance(result_text, textstyler.StyledText): + serialized_result = result_text.to_json() + result_format = "styled_text_json" + else: + serialized_result = result_text + result_format = "string" + else: + raise ActionFailedException( + f"Unsupported result format: {options.result_format}" + ) + elif action_result is not None: + logger.error( + f"R{run_id} | Unexpected result type: {type(action_result).__name__}" + ) + raise ActionFailedException( + f"Unexpected result type: {type(action_result).__name__}" + ) + + end_time = time.time_ns() + duration = (end_time - start_time) / 1_000_000 + logger.trace( + f"R{run_id} | Run action end '{request.action_name}', duration: {duration}ms" + ) + return schemas.RunActionResponse( + result=serialized_result, + format=result_format, + return_code=run_return_code.value, + ) + + + +def create_action_exec_info(action: domain.Action) -> domain.ActionExecInfo: + try: + action_type_def = run_utils.import_module_member_by_source_str(action.source) + except Exception as e: + logger.error(f"Error importing action type: {e}") + raise e + + if not issubclass(action_type_def, code_action.Action): + raise Exception("Action class expected to be a subclass of finecode_extension_api.code_action.Action") + + payload_type = action_type_def.PAYLOAD_TYPE + run_context_type = action_type_def.RUN_CONTEXT_TYPE + + + # TODO: validate that classes and correct subclasses? + + action_exec_info = domain.ActionExecInfo( + payload_type=payload_type, run_context_type=run_context_type + ) + return action_exec_info + + + +def resolve_func_args_with_di( + func: typing.Callable, + known_args: dict[str, typing.Callable[[typing.Any], typing.Any]] | None = None, + params_to_ignore: list[str] | None = None, +): + func_parameters = inspect.signature(func).parameters + func_annotations = inspect.get_annotations(func, eval_str=True) + args: dict[str, typing.Any] = {} + for param_name in func_parameters.keys(): + # default object constructor(__init__) has signature + # __init__(self, *args, **kwargs) + # args and kwargs have no annotation and should not be filled by DI resolver. + # Ignore them. + if ( + params_to_ignore is not None and param_name in params_to_ignore + ) or param_name in ["args", "kwargs"]: + continue + elif known_args is not None and param_name in known_args: + param_type = func_annotations[param_name] + # value in known args is a callable factory to instantiate param value + args[param_name] = known_args[param_name](param_type) + else: + # TODO: handle errors + param_type = func_annotations[param_name] + param_value = di_resolver.get_service_instance(param_type) + args[param_name] = param_value + + return args + + + +async def execute_action_handler( + handler: domain.ActionHandler, + payload: code_action.RunActionPayload | None, + run_context: code_action.RunActionContext | None, + run_id: int, + action_exec_info: domain.ActionExecInfo, + action_context: code_action.ActionContext, + runner_context: context.RunnerContext, +) -> code_action.RunActionResult | None: + logger.trace(f"R{run_id} | Run {handler.name} on {str(payload)[:100]}...") + start_time = time.time_ns() + execution_result: code_action.RunActionResult | None = None + + if handler.name in runner_context.action_handlers_instances_by_name: + handler_instance = runner_context.action_handlers_instances_by_name[ + handler.name + ] + handler_run_func = handler_instance.run + exec_info = runner_context.action_handlers_exec_info_by_name[handler.name] + logger.trace( + f"R{run_id} | Instance of action handler {handler.name} found in cache" + ) + else: + logger.trace(f"R{run_id} | Load action handler {handler.name}") + try: + action_handler = run_utils.import_module_member_by_source_str( + handler.source + ) + except ModuleNotFoundError as error: + logger.error( + f"R{run_id} | Source of action handler {handler.name} '{handler.source}'" + " could not be imported" + ) + logger.error(error) + return None + + handler_raw_config = handler.config + + def get_handler_config(param_type): + # TODO: validation errors + return param_type(**handler_raw_config) + + def get_action_context(param_type): + return action_context + + def get_process_executor(param_type): + return action_exec_info.process_executor + + exec_info = domain.ActionHandlerExecInfo() + # save immediately in context to be able to shutdown it if the first execution + # is interrupted by stopping ER + runner_context.action_handlers_exec_info_by_name[handler.name] = exec_info + if inspect.isclass(action_handler): + args = resolve_func_args_with_di( + func=action_handler.__init__, + known_args={ + "config": get_handler_config, + "context": get_action_context, + "process_executor": get_process_executor, + }, + params_to_ignore=["self"], + ) + + if "lifecycle" in args: + exec_info.lifecycle = args["lifecycle"] + + handler_instance = action_handler(**args) + runner_context.action_handlers_instances_by_name[handler.name] = ( + handler_instance + ) + handler_run_func = handler_instance.run + else: + handler_run_func = action_handler + + if ( + exec_info.lifecycle is not None + and exec_info.lifecycle.on_initialize_callable is not None + ): + logger.trace(f"R{run_id} | Initialize {handler.name} action handler") + try: + initialize_callable_result = ( + exec_info.lifecycle.on_initialize_callable() + ) + if inspect.isawaitable(initialize_callable_result): + await initialize_callable_result + except Exception as e: + logger.error( + f"R{run_id} | Failed to initialize action {handler.name}: {e}" + ) + return None + exec_info.status = domain.ActionHandlerExecInfoStatus.INITIALIZED + + def get_run_payload(param_type): + return payload + + def get_run_context(param_type): + return run_context + + # DI in `run` function is allowed only for action handlers in form of functions. + # `run` in classes may not have additional parameters, constructor parameters should + # be used instead. TODO: Validate? + args = resolve_func_args_with_di( + func=handler_run_func, + known_args={"payload": get_run_payload, "run_context": get_run_context}, + ) + # TODO: cache parameters + try: + # there is also `inspect.iscoroutinefunction` but it cannot recognize coroutine + # functions which are class methods. Use `isawaitable` on result instead. + call_result = handler_run_func(**args) + if inspect.isawaitable(call_result): + execution_result = await call_result + else: + execution_result = call_result + except Exception as e: + logger.exception(e) + raise ActionFailedException( + f"Running action handler '{handler.name}' failed(Run {run_id}): {e}" + ) + + end_time = time.time_ns() + duration = (end_time - start_time) / 1_000_000 + logger.trace( + f"R{run_id} | End of execution of action handler {handler.name}" + f" on {str(payload)[:100]}..., duration: {duration}ms" + ) + return execution_result + + + +async def run_subresult_coros_concurrently( + coros: list[collections.abc.Coroutine], + send_partial_results: bool, + partial_result_token: int | str, + partial_result_sender: partial_result_sender_module.PartialResultSender, + action_name: str, + run_id: int, +) -> code_action.RunActionResult | None: + coros_tasks: list[asyncio.Task] = [] + try: + async with asyncio.TaskGroup() as tg: + for coro in coros: + coro_task = tg.create_task(coro) + coros_tasks.append(coro_task) + except ExceptionGroup as eg: + logger.error(f"R{run_id} | {eg}") + for exc in eg.exceptions: + logger.exception(exc) + raise ActionFailedException( + f"Concurrent running action handlers of '{action_name}' failed(Run {run_id}). See logs for more details" + ) + + action_subresult: code_action.RunActionResult | None = None + for coro_task in coros_tasks: + coro_result = coro_task.result() + if coro_result is not None: + if action_subresult is None: + action_subresult = coro_result + else: + action_subresult.update(coro_result) + + if send_partial_results: + await partial_result_sender.schedule_sending( + partial_result_token, action_subresult + ) + return None + else: + return action_subresult + + +async def run_subresult_coros_sequentially( + coros: list[collections.abc.Coroutine], + send_partial_results: bool, + partial_result_token: int | str, + partial_result_sender: partial_result_sender_module.PartialResultSender, + action_name: str, + run_id: int, +) -> code_action.RunActionResult | None: + action_subresult: code_action.RunActionResult | None = None + for coro in coros: + try: + coro_result = await coro + except Exception as e: + logger.exception(e) + raise ActionFailedException( + f"Running action handlers of '{action_name}' failed(Run {run_id}): {e}" + ) + + if coro_result is not None: + if action_subresult is None: + action_subresult = coro_result + else: + action_subresult.update(coro_result) + + if send_partial_results: + await partial_result_sender.schedule_sending( + partial_result_token, action_subresult + ) + return None + else: + return action_subresult diff --git a/src/finecode/extension_runner/action_handlers/__init__.py b/src/finecode/extension_runner/action_handlers/__init__.py new file mode 100644 index 0000000..857436f --- /dev/null +++ b/src/finecode/extension_runner/action_handlers/__init__.py @@ -0,0 +1,9 @@ +from .dump_config import DumpConfigHandler +from .prepare_envs_dump_configs import PrepareEnvsDumpConfigsHandler +from .dump_config_save import DumpConfigSaveHandler + +__all__ = [ + 'DumpConfigHandler', + 'PrepareEnvsDumpConfigsHandler', + 'DumpConfigSaveHandler', +] diff --git a/src/finecode/extension_runner/action_handlers/dump_config.py b/src/finecode/extension_runner/action_handlers/dump_config.py new file mode 100644 index 0000000..908c5c8 --- /dev/null +++ b/src/finecode/extension_runner/action_handlers/dump_config.py @@ -0,0 +1,26 @@ +import dataclasses + +import tomlkit + +from finecode_extension_api import code_action +from finecode_extension_api.actions import dump_config as dump_config_action +from finecode_extension_api.interfaces import ifilemanager + + +@dataclasses.dataclass +class DumpConfigHandlerConfig(code_action.ActionHandlerConfig): ... + + +class DumpConfigHandler( + code_action.ActionHandler[dump_config_action.DumpConfigAction, DumpConfigHandlerConfig] +): + async def run( + self, payload: dump_config_action.DumpConfigRunPayload, run_context: dump_config_action.DumpConfigRunContext + ) -> dump_config_action.DumpConfigRunResult: + # presets are resolved, remove tool.finecode.presets key to avoid repeating + # resolving if dump config is processed + finecode_config = run_context.raw_config_dump.get('tool', {}).get('finecode', {}) + if 'presets' in finecode_config: + del finecode_config['presets'] + + return dump_config_action.DumpConfigRunResult() diff --git a/src/finecode/extension_runner/action_handlers/dump_config_save.py b/src/finecode/extension_runner/action_handlers/dump_config_save.py new file mode 100644 index 0000000..b766a95 --- /dev/null +++ b/src/finecode/extension_runner/action_handlers/dump_config_save.py @@ -0,0 +1,32 @@ +import dataclasses + +import tomlkit + +from finecode_extension_api import code_action +from finecode_extension_api.actions import dump_config as dump_config_action +from finecode_extension_api.interfaces import ifilemanager + + +@dataclasses.dataclass +class DumpConfigSaveHandlerConfig(code_action.ActionHandlerConfig): ... + + +class DumpConfigSaveHandler( + code_action.ActionHandler[dump_config_action.DumpConfigAction, DumpConfigSaveHandlerConfig] +): + def __init__( + self, + file_manager: ifilemanager.IFileManager, + ) -> None: + self.file_manager = file_manager + + async def run( + self, payload: dump_config_action.DumpConfigRunPayload, run_context: dump_config_action.DumpConfigRunContext + ) -> dump_config_action.DumpConfigRunResult: + raw_config_str = tomlkit.dumps(run_context.raw_config_dump) + + await self.file_manager.save_file( + file_path=payload.target_file_path, file_content=raw_config_str + ) + + return dump_config_action.DumpConfigRunResult() diff --git a/src/finecode/extension_runner/action_handlers/prepare_envs_dump_configs.py b/src/finecode/extension_runner/action_handlers/prepare_envs_dump_configs.py new file mode 100644 index 0000000..c67045a --- /dev/null +++ b/src/finecode/extension_runner/action_handlers/prepare_envs_dump_configs.py @@ -0,0 +1,76 @@ +import dataclasses +import shutil + +import tomlkit + +from finecode_extension_api import code_action +from finecode_extension_api.actions import prepare_envs as prepare_envs_action +from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger + + +@dataclasses.dataclass +class PrepareEnvsDumpConfigsHandlerConfig(code_action.ActionHandlerConfig): ... + + +class PrepareEnvsDumpConfigsHandler( + code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, PrepareEnvsDumpConfigsHandlerConfig] +): + def __init__(self, action_runner: iactionrunner.IActionRunner, project_info_provider: iprojectinfoprovider.IProjectInfoProvider, logger: ilogger.ILogger) -> None: + self.action_runner = action_runner + self.project_info_provider = project_info_provider + self.logger = logger + + async def run( + self, payload: prepare_envs_action.PrepareEnvsRunPayload, run_context: prepare_envs_action.PrepareEnvsRunContext + ) -> prepare_envs_action.PrepareEnvsRunResult: + project_defs_pathes = set([env_info.project_def_path for env_info in payload.envs]) + if len(project_defs_pathes) != 1: + ... # TODO: error + + project_raw_config = await self.project_info_provider.get_project_raw_config() + + project_def_path = project_defs_pathes.pop() + project_dir_path = project_def_path.parent + # TODO: unify with call of dump_config in CLI + dump_dir_path = project_dir_path / 'finecode_config_dump' + try: + await self.action_runner.run_action(name='dump_config', payload={ + "source_file_path": project_def_path, + "project_raw_config": project_raw_config, + "target_file_path": dump_dir_path / 'pyproject.toml' + }) + new_project_def_path = dump_dir_path / 'pyproject.toml' + for env_info in payload.envs: + run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = new_project_def_path + except iactionrunner.BaseRunActionException as exception: + self.logger.exception(exception) # TODO + + # after dumping config in another directory, pathes to project files like + # readme and source files are wrong. We cannot just change the pathes to the new + # one in project configuration, because they would be outside of the project(in + # parent directory) and pip doesn't support this. Instead, we create symlinks + # to all project files in the directory with dumped config. + # - readme is needed to avoid error that it is missing + # - source files are needed for runtime environment. During installation of + # requirements, pip automatically resolves symlinks and editable pathes point + # to original source files, not to temporary symlinks + # + # question: filemanager should be used here? + for item in project_dir_path.iterdir(): + if item.name == 'finecode_config_dump' or item.name == 'pyproject.toml': + # ignore: + # - dir with dumped config + # - dumped config + continue + + new_item_path = dump_dir_path / item.name + if new_item_path.exists(): + if new_item_path.is_symlink(): + new_item_path.unlink() + elif new_item_path.is_dir(): + shutil.rmtree(new_item_path) + else: + new_item_path.unlink() + new_item_path.symlink_to(item, target_is_directory=item.is_dir()) + + return prepare_envs_action.PrepareEnvsRunResult(results=[]) # TODO diff --git a/src/finecode/extension_runner/bootstrap.py b/src/finecode/extension_runner/bootstrap.py deleted file mode 100644 index 4a5e4cd..0000000 --- a/src/finecode/extension_runner/bootstrap.py +++ /dev/null @@ -1,90 +0,0 @@ -""" -Requirements on DI in FineCode: -- using interfaces in functions and injecting implementations -- all implementations are singletons --- but technically it should be possible to add non-singletons -- default container is known -- default container can be customized at one place, no dynamic changes -""" - -from typing import Any, Callable, Type, TypeVar - -try: - import fine_python_ast -except ImportError: - fine_python_ast = None - -try: - import fine_python_mypy -except ImportError: - fine_python_mypy = None - -from finecode.extension_runner import global_state -from finecode.extension_runner.impls import ( - command_runner, - file_manager, - inmemory_cache, - loguru_logger, -) -from finecode_extension_api import code_action -from finecode_extension_api.interfaces import ( - icache, - icommandrunner, - ifilemanager, - ilogger, -) - -container: dict[str, Any] = {} - - -def bootstrap(get_document_func: Callable, save_document_func: Callable): - # logger_instance = loguru_logger.LoguruLogger() - logger_instance = loguru_logger.get_logger() - command_runner_instance = command_runner.CommandRunner(logger=logger_instance) - file_manager_instance = file_manager.FileManager( - docs_owned_by_client=global_state.runner_context.docs_owned_by_client, - get_document_func=get_document_func, - save_document_func=save_document_func, - logger=logger_instance, - ) - cache_instance = inmemory_cache.InMemoryCache( - file_manager=file_manager_instance, logger=logger_instance - ) - container[ilogger.ILogger] = logger_instance - container[icommandrunner.ICommandRunner] = command_runner_instance - container[ifilemanager.IFileManager] = file_manager_instance - container[icache.ICache] = cache_instance - - # TODO: parameters from config - ... - - -T = TypeVar("T") - - -def get_service_instance(service_type: Type[T]) -> T: - if service_type == code_action.ActionHandlerLifecycle: - return code_action.ActionHandlerLifecycle() - - # singletons - if service_type in container: - return container[service_type] - else: - match service_type: - case fine_python_ast.IPythonSingleAstProvider: - service_instance = fine_python_ast.PythonSingleAstProvider( - file_manager=container[ifilemanager.IFileManager], - cache=container[icache.ICache], - logger=container[ilogger.ILogger], - ) - case fine_python_mypy.IMypySingleAstProvider: - service_instance = fine_python_mypy.MypySingleAstProvider( - file_manager=container[ifilemanager.IFileManager], - cache=container[icache.ICache], - logger=container[ilogger.ILogger], - ) - case _: - raise NotImplementedError(f"No implementation found for {service_type}") - - container[service_type] = service_instance - return service_instance diff --git a/src/finecode/extension_runner/di/__init__.py b/src/finecode/extension_runner/di/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/finecode/extension_runner/di/_state.py b/src/finecode/extension_runner/di/_state.py new file mode 100644 index 0000000..7c3bac8 --- /dev/null +++ b/src/finecode/extension_runner/di/_state.py @@ -0,0 +1,5 @@ +import typing + + +container: dict[str, typing.Any] = {} +factories: dict[str, typing.Callable] = {} diff --git a/src/finecode/extension_runner/di/bootstrap.py b/src/finecode/extension_runner/di/bootstrap.py new file mode 100644 index 0000000..43a8ade --- /dev/null +++ b/src/finecode/extension_runner/di/bootstrap.py @@ -0,0 +1,92 @@ + +from typing import Any, Callable, Type, TypeVar + +try: + import fine_python_ast +except ImportError: + fine_python_ast = None + +try: + import fine_python_mypy +except ImportError: + fine_python_mypy = None + +from finecode.extension_runner import global_state +from finecode.extension_runner.impls import ( + action_runner, + command_runner, + file_manager, + inmemory_cache, + loguru_logger, + project_info_provider +) +from finecode_extension_api import code_action +from finecode_extension_api.interfaces import ( + iactionrunner, + icache, + icommandrunner, + ifilemanager, + ilogger, + iprojectinfoprovider +) +from ._state import container, factories +from finecode.extension_runner import schemas +from finecode.extension_runner._services import run_action + + +def bootstrap(get_document_func: Callable, save_document_func: Callable): + # logger_instance = loguru_logger.LoguruLogger() + logger_instance = loguru_logger.get_logger() + command_runner_instance = command_runner.CommandRunner(logger=logger_instance) + file_manager_instance = file_manager.FileManager( + docs_owned_by_client=global_state.runner_context.docs_owned_by_client, + get_document_func=get_document_func, + save_document_func=save_document_func, + logger=logger_instance, + ) + cache_instance = inmemory_cache.InMemoryCache( + file_manager=file_manager_instance, logger=logger_instance + ) + action_runner_instance = action_runner.ActionRunner(internal_service_func=run_action_wrapper) + container[ilogger.ILogger] = logger_instance + container[icommandrunner.ICommandRunner] = command_runner_instance + container[ifilemanager.IFileManager] = file_manager_instance + container[icache.ICache] = cache_instance + container[iactionrunner.IActionRunner] = action_runner_instance + + if fine_python_ast is not None: + factories[fine_python_ast.IPythonSingleAstProvider] = python_single_ast_provider_factory + if fine_python_mypy is not None: + factories[fine_python_mypy.IMypySingleAstProvider] = mypy_single_ast_provider_factory + factories[iprojectinfoprovider.IProjectInfoProvider] = project_info_provider_factory + + # TODO: parameters from config + +async def run_action_wrapper(action_name: str, payload: dict[str, Any]) -> dict[str, Any]: + request = schemas.RunActionRequest(action_name=action_name, params=payload) + options = schemas.RunActionOptions(result_format='json') + response = await run_action.run_action(request=request, options=options) + if response.return_code != 0: + # TODO: pass details + raise iactionrunner.ActionRunFailed() + + return response.result + + +def python_single_ast_provider_factory(container): + return fine_python_ast.PythonSingleAstProvider( + file_manager=container[ifilemanager.IFileManager], + cache=container[icache.ICache], + logger=container[ilogger.ILogger], + ) + +def mypy_single_ast_provider_factory(container): + return fine_python_mypy.MypySingleAstProvider( + file_manager=container[ifilemanager.IFileManager], + cache=container[icache.ICache], + logger=container[ilogger.ILogger], + ) + + +def project_info_provider_factory(container): + return project_info_provider.ProjectInfoProvider() diff --git a/src/finecode/extension_runner/di/resolver.py b/src/finecode/extension_runner/di/resolver.py new file mode 100644 index 0000000..fb5e4e7 --- /dev/null +++ b/src/finecode/extension_runner/di/resolver.py @@ -0,0 +1,23 @@ +from typing import Any, Callable, Type, TypeVar + +from finecode_extension_api import code_action +from ._state import container, factories + +T = TypeVar("T") + + +def get_service_instance(service_type: Type[T]) -> T: + if service_type == code_action.ActionHandlerLifecycle: + return code_action.ActionHandlerLifecycle() + + # singletons + if service_type in container: + return container[service_type] + else: + if service_type in factories: + service_instance = factories[service_type](container) + else: + raise ValueError(f"No implementation found for {service_type}") + + container[service_type] = service_instance + return service_instance diff --git a/src/finecode/extension_runner/impls/action_runner.py b/src/finecode/extension_runner/impls/action_runner.py new file mode 100644 index 0000000..dda1271 --- /dev/null +++ b/src/finecode/extension_runner/impls/action_runner.py @@ -0,0 +1,17 @@ +from pathlib import Path +from typing import Any, TypeAlias + +from finecode_extension_api.interfaces import iactionrunner + + +class ActionRunner(iactionrunner.IActionRunner): + def __init__( + self, + internal_service_func + ): + self._internal_service_func = internal_service_func + + async def run_action( + self, name: str, payload: dict[str, Any] + ) -> dict[str, Any]: + return await self._internal_service_func(action_name=name, payload=payload) diff --git a/src/finecode/extension_runner/impls/project_info_provider.py b/src/finecode/extension_runner/impls/project_info_provider.py new file mode 100644 index 0000000..6b8d8c9 --- /dev/null +++ b/src/finecode/extension_runner/impls/project_info_provider.py @@ -0,0 +1,19 @@ +from typing import Callable +from typing import Any + +from finecode_extension_api.interfaces import iprojectinfoprovider + + +project_raw_config_getter: Callable + + +class ProjectInfoProvider(iprojectinfoprovider.IProjectInfoProvider): + def __init__( + self, + ) -> None: + ... + + async def get_project_raw_config( + self + ) -> dict[str, Any]: + return await project_raw_config_getter() diff --git a/src/finecode/extension_runner/lsp_server.py b/src/finecode/extension_runner/lsp_server.py index 4f89f6d..baf52f1 100644 --- a/src/finecode/extension_runner/lsp_server.py +++ b/src/finecode/extension_runner/lsp_server.py @@ -16,6 +16,8 @@ from pygls.lsp import server as lsp_server from finecode.extension_runner import domain, schemas, services +from finecode.extension_runner._services import run_action as run_action_service +from finecode.extension_runner.impls import project_info_provider from finecode_extension_api import code_action @@ -122,7 +124,19 @@ def send_partial_result( services.document_requester = document_requester services.document_saver = document_saver - services.set_partial_result_sender(send_partial_result) + run_action_service.set_partial_result_sender(send_partial_result) + + async def get_project_raw_config(): + try: + raw_config = await server.protocol.send_request_async( + "projects/getRawConfig", params={} + ) + except pygls_exceptions.JsonRpcInternalError as error: + raise error + + return json.loads(raw_config.config) + + project_info_provider.project_raw_config_getter = get_project_raw_config return server diff --git a/src/finecode/extension_runner/services.py b/src/finecode/extension_runner/services.py index 4160947..7faa3c7 100644 --- a/src/finecode/extension_runner/services.py +++ b/src/finecode/extension_runner/services.py @@ -11,27 +11,16 @@ from loguru import logger from pydantic.dataclasses import dataclass as pydantic_dataclass -from finecode.extension_runner import bootstrap, context, domain, global_state -from finecode.extension_runner import ( - partial_result_sender as partial_result_sender_module, -) +from finecode.extension_runner import context, domain, global_state from finecode.extension_runner import project_dirs, run_utils, schemas from finecode_extension_api import code_action, textstyler - - -class ActionFailedException(Exception): ... +from finecode.extension_runner._services import run_action as run_action_module +from finecode.extension_runner._services.run_action import run_action +from finecode.extension_runner.di import bootstrap as di_bootstrap document_requester: typing.Callable document_saver: typing.Callable -partial_result_sender: partial_result_sender_module.PartialResultSender - - -def set_partial_result_sender(send_func: typing.Callable) -> None: - global partial_result_sender - partial_result_sender = partial_result_sender_module.PartialResultSender( - sender=send_func, wait_time_ms=300 - ) async def get_document(uri: str): @@ -77,520 +66,13 @@ async def update_config( # currently update_config is called only once directly after runner start. So we can # bootstrap here. Should be changed after adding updating configuration on the fly. - bootstrap.bootstrap( + di_bootstrap.bootstrap( get_document_func=get_document, save_document_func=save_document ) return schemas.UpdateConfigResponse() -def resolve_func_args_with_di( - func: typing.Callable, - known_args: dict[str, typing.Callable[[typing.Any], typing.Any]] | None = None, - params_to_ignore: list[str] | None = None, -): - func_parameters = inspect.signature(func).parameters - func_annotations = inspect.get_annotations(func, eval_str=True) - args: dict[str, typing.Any] = {} - for param_name in func_parameters.keys(): - # default object constructor(__init__) has signature - # __init__(self, *args, **kwargs) - # args and kwargs have no annotation and should not be filled by DI resolver. - # Ignore them. - if ( - params_to_ignore is not None and param_name in params_to_ignore - ) or param_name in ["args", "kwargs"]: - continue - elif known_args is not None and param_name in known_args: - param_type = func_annotations[param_name] - # value in known args is a callable factory to instantiate param value - args[param_name] = known_args[param_name](param_type) - else: - # TODO: handle errors - param_type = func_annotations[param_name] - param_value = bootstrap.get_service_instance(param_type) - args[param_name] = param_value - - return args - - -def create_action_exec_info(action: domain.Action) -> domain.ActionExecInfo: - try: - action_type_def = run_utils.import_module_member_by_source_str(action.source) - except Exception as e: - logger.error(f"Error importing action type: {e}") - raise e - - if not issubclass(action_type_def, code_action.Action): - raise Exception("Action class expected to be a subclass of finecode_extension_api.code_action.Action") - - payload_type = action_type_def.PAYLOAD_TYPE - run_context_type = action_type_def.RUN_CONTEXT_TYPE - - - # TODO: validate that classes and correct subclasses? - - action_exec_info = domain.ActionExecInfo( - payload_type=payload_type, run_context_type=run_context_type - ) - return action_exec_info - - -async def run_subresult_coros_concurrently( - coros: list[collections.abc.Coroutine], - send_partial_results: bool, - partial_result_token: int | str, - partial_result_sender: partial_result_sender_module.PartialResultSender, - action_name: str, - run_id: int, -) -> code_action.RunActionResult | None: - coros_tasks: list[asyncio.Task] = [] - try: - async with asyncio.TaskGroup() as tg: - for coro in coros: - coro_task = tg.create_task(coro) - coros_tasks.append(coro_task) - except ExceptionGroup as eg: - logger.error(f"R{run_id} | {eg}") - for exc in eg.exceptions: - logger.exception(exc) - raise ActionFailedException( - f"Concurrent running action handlers of '{action_name}' failed(Run {run_id}). See logs for more details" - ) - - action_subresult: code_action.RunActionResult | None = None - for coro_task in coros_tasks: - coro_result = coro_task.result() - if coro_result is not None: - if action_subresult is None: - action_subresult = coro_result - else: - action_subresult.update(coro_result) - - if send_partial_results: - await partial_result_sender.schedule_sending( - partial_result_token, action_subresult - ) - return None - else: - return action_subresult - - -async def run_subresult_coros_sequentially( - coros: list[collections.abc.Coroutine], - send_partial_results: bool, - partial_result_token: int | str, - partial_result_sender: partial_result_sender_module.PartialResultSender, - action_name: str, - run_id: int, -) -> code_action.RunActionResult | None: - action_subresult: code_action.RunActionResult | None = None - for coro in coros: - try: - coro_result = await coro - except Exception as e: - logger.exception(e) - raise ActionFailedException( - f"Running action handlers of '{action_name}' failed(Run {run_id}): {e}" - ) - - if coro_result is not None: - if action_subresult is None: - action_subresult = coro_result - else: - action_subresult.update(coro_result) - - if send_partial_results: - await partial_result_sender.schedule_sending( - partial_result_token, action_subresult - ) - return None - else: - return action_subresult - - -last_run_id: int = 0 - - -async def run_action( - request: schemas.RunActionRequest, options: schemas.RunActionOptions -) -> schemas.RunActionResponse: - global last_run_id - run_id = last_run_id - last_run_id += 1 - logger.trace(f"Run action '{request.action_name}', run id: {run_id}") - # TODO: check whether config is set: this will be solved by passing initial - # configuration as payload of initialize - if global_state.runner_context is None: - raise ActionFailedException( - "Run of action failed because extension runner is not initialized yet" - ) - - start_time = time.time_ns() - project_def = global_state.runner_context.project - - try: - action = project_def.actions[request.action_name] - except KeyError: - logger.error(f"R{run_id} | Action {request.action_name} not found") - raise ActionFailedException( - f"R{run_id} | Action {request.action_name} not found" - ) - - # design decisions: - # - keep payload unchanged between all subaction runs. - # For intermediate data use run_context - # - result is modifiable. Result of each subaction updates the previous result. - # In case of failure of subaction, at least result of all previous handlers is - # returned. (experimental) - # - execution of handlers can be concurrent or sequential. But executions of handler - # on iterable payloads(single parts) are always concurrent. - - try: - action_exec_info = global_state.runner_context.action_exec_info_by_name[ - request.action_name - ] - except KeyError: - action_exec_info = create_action_exec_info(action) - global_state.runner_context.action_exec_info_by_name[request.action_name] = ( - action_exec_info - ) - - # TODO: catch validation errors - payload: code_action.RunActionPayload | None = None - if action_exec_info.payload_type is not None: - payload_type_with_validation = pydantic_dataclass(action_exec_info.payload_type) - payload = payload_type_with_validation(**request.params) - - run_context: code_action.RunActionContext | None = None - if action_exec_info.run_context_type is not None: - constructor_args = resolve_func_args_with_di( - action_exec_info.run_context_type.__init__, - known_args={"run_id": lambda _: run_id}, - params_to_ignore=["self"], - ) - - run_context = action_exec_info.run_context_type(**constructor_args) - # TODO: handler errors - await run_context.init(initial_payload=payload) - - action_result: code_action.RunActionResult | None = None - runner_context = global_state.runner_context - - # instantiate only on demand? - project_path = project_def.path - project_cache_dir = project_dirs.get_project_dir(project_path=project_path) - action_context = code_action.ActionContext( - project_dir=project_path, cache_dir=project_cache_dir - ) - - # TODO: take value from action config - execute_handlers_concurrently = action.name == "lint" - partial_result_token = options.partial_result_token - send_partial_results = partial_result_token is not None - with action_exec_info.process_executor.activate(): - # action payload can be iterable or not - if isinstance(payload, collections.abc.AsyncIterable): - # iterable: `run` method should not calculate results itself, but call - # `partial_result_scheduler.schedule`. Then we execute provided - # coroutines either concurrently or sequentially. - logger.trace( - f"R{run_id} | Iterable payload, execute all handlers to schedule coros" - ) - for handler in action.handlers: - await execute_action_handler( - handler=handler, - payload=payload, - run_context=run_context, - run_id=run_id, - action_context=action_context, - action_exec_info=action_exec_info, - runner_context=runner_context, - ) - - parts = [part async for part in payload] - subresults_tasks: list[asyncio.Task] = [] - logger.trace( - "R{run_id} | Run subresult coros {exec_type} {partials} partial results".format( - run_id=run_id, - exec_type=( - "concurrently" - if execute_handlers_concurrently - else "sequentially" - ), - partials="with" if send_partial_results else "without", - ) - ) - try: - async with asyncio.TaskGroup() as tg: - for part in parts: - part_coros = ( - run_context.partial_result_scheduler.coroutines_by_key[part] - ) - del run_context.partial_result_scheduler.coroutines_by_key[part] - if execute_handlers_concurrently: - coro = run_subresult_coros_concurrently( - part_coros, - send_partial_results, - partial_result_token, - partial_result_sender, - action.name, - run_id, - ) - else: - coro = run_subresult_coros_sequentially( - part_coros, - send_partial_results, - partial_result_token, - partial_result_sender, - action.name, - run_id, - ) - subresult_task = tg.create_task(coro) - subresults_tasks.append(subresult_task) - except ExceptionGroup as eg: - logger.error(eg) - for exc in eg.exceptions: - logger.exception(exc) - raise ActionFailedException( - f"Running action handlers of '{action.name}' failed(Run {run_id})." - " See ER logs for more details" - ) - - if send_partial_results: - # all subresults are ready - logger.trace(f"R{run_id} | all subresults are ready, send them") - await partial_result_sender.send_all_immediately() - - for subresult_task in subresults_tasks: - result = subresult_task.result() - if result is not None: - if action_result is None: - action_result = result - else: - action_result.update(result) - else: - # action payload not iterable, just execute handlers on the whole payload - if execute_handlers_concurrently: - handlers_tasks: list[asyncio.Task] = [] - try: - async with asyncio.TaskGroup() as tg: - for handler in action.handlers: - handler_task = tg.create_task( - execute_action_handler( - handler=handler, - payload=payload, - run_context=run_context, - run_id=run_id, - action_context=action_context, - action_exec_info=action_exec_info, - runner_context=runner_context, - ) - ) - handlers_tasks.append(handler_task) - except ExceptionGroup as eg: - logger.error(eg) - for exc in eg.exceptions: - logger.exception(exc) - raise ActionFailedException( - f"Running action handlers of '{action.name}' failed" - f"(Run {run_id}). See ER logs for more details" - ) - - for handler_task in handlers_tasks: - coro_result = handler_task.result() - if coro_result is not None: - if action_result is None: - action_result = coro_result - else: - action_result.update(coro_result) - else: - for handler in action.handlers: - handler_result = await execute_action_handler( - handler=handler, - payload=payload, - run_context=run_context, - run_id=run_id, - action_context=action_context, - action_exec_info=action_exec_info, - runner_context=runner_context, - ) - - if handler_result is not None: - if action_result is None: - action_result = handler_result - else: - action_result.update(handler_result) - - serialized_result: dict[str, typing.Any] | str | None = None - result_format = "string" - run_return_code = code_action.RunReturnCode.SUCCESS - if isinstance(action_result, code_action.RunActionResult): - run_return_code = action_result.return_code - if options.result_format == "json": - serialized_result = action_result.model_dump(mode="json") - result_format = "json" - elif options.result_format == "string": - result_text = action_result.to_text() - if isinstance(result_text, textstyler.StyledText): - serialized_result = result_text.to_json() - result_format = "styled_text_json" - else: - serialized_result = result_text - result_format = "string" - else: - raise ActionFailedException( - f"Unsupported result format: {options.result_format}" - ) - elif action_result is not None: - logger.error( - f"R{run_id} | Unexpected result type: {type(action_result).__name__}" - ) - raise ActionFailedException( - f"Unexpected result type: {type(action_result).__name__}" - ) - - end_time = time.time_ns() - duration = (end_time - start_time) / 1_000_000 - logger.trace( - f"R{run_id} | Run action end '{request.action_name}', duration: {duration}ms" - ) - return schemas.RunActionResponse( - result=serialized_result, - format=result_format, - return_code=run_return_code.value, - ) - - -async def execute_action_handler( - handler: domain.ActionHandler, - payload: code_action.RunActionPayload | None, - run_context: code_action.RunActionContext | None, - run_id: int, - action_exec_info: domain.ActionExecInfo, - action_context: code_action.ActionContext, - runner_context: context.RunnerContext, -) -> code_action.RunActionResult | None: - logger.trace(f"R{run_id} | Run {handler.name} on {str(payload)[:100]}...") - start_time = time.time_ns() - execution_result: code_action.RunActionResult | None = None - - if handler.name in runner_context.action_handlers_instances_by_name: - handler_instance = runner_context.action_handlers_instances_by_name[ - handler.name - ] - handler_run_func = handler_instance.run - exec_info = runner_context.action_handlers_exec_info_by_name[handler.name] - logger.trace( - f"R{run_id} | Instance of action handler {handler.name} found in cache" - ) - else: - logger.trace(f"R{run_id} | Load action handler {handler.name}") - try: - action_handler = run_utils.import_module_member_by_source_str( - handler.source - ) - except ModuleNotFoundError as error: - logger.error( - f"R{run_id} | Source of action handler {handler.name} '{handler.source}'" - " could not be imported" - ) - logger.error(error) - return None - - handler_raw_config = handler.config - - def get_handler_config(param_type): - # TODO: validation errors - return param_type(**handler_raw_config) - - def get_action_context(param_type): - return action_context - - def get_process_executor(param_type): - return action_exec_info.process_executor - - exec_info = domain.ActionHandlerExecInfo() - # save immediately in context to be able to shutdown it if the first execution - # is interrupted by stopping ER - runner_context.action_handlers_exec_info_by_name[handler.name] = exec_info - if inspect.isclass(action_handler): - args = resolve_func_args_with_di( - func=action_handler.__init__, - known_args={ - "config": get_handler_config, - "context": get_action_context, - "process_executor": get_process_executor, - }, - params_to_ignore=["self"], - ) - - if "lifecycle" in args: - exec_info.lifecycle = args["lifecycle"] - - handler_instance = action_handler(**args) - runner_context.action_handlers_instances_by_name[handler.name] = ( - handler_instance - ) - handler_run_func = handler_instance.run - else: - handler_run_func = action_handler - - if ( - exec_info.lifecycle is not None - and exec_info.lifecycle.on_initialize_callable is not None - ): - logger.trace(f"R{run_id} | Initialize {handler.name} action handler") - try: - initialize_callable_result = ( - exec_info.lifecycle.on_initialize_callable() - ) - if inspect.isawaitable(initialize_callable_result): - await initialize_callable_result - except Exception as e: - logger.error( - f"R{run_id} | Failed to initialize action {handler.name}: {e}" - ) - return None - exec_info.status = domain.ActionHandlerExecInfoStatus.INITIALIZED - - def get_run_payload(param_type): - return payload - - def get_run_context(param_type): - return run_context - - # DI in `run` function is allowed only for action handlers in form of functions. - # `run` in classes may not have additional parameters, constructor parameters should - # be used instead. TODO: Validate? - args = resolve_func_args_with_di( - func=handler_run_func, - known_args={"payload": get_run_payload, "run_context": get_run_context}, - ) - # TODO: cache parameters - try: - # there is also `inspect.iscoroutinefunction` but it cannot recognize coroutine - # functions which are class methods. Use `isawaitable` on result instead. - call_result = handler_run_func(**args) - if inspect.isawaitable(call_result): - execution_result = await call_result - else: - execution_result = call_result - except Exception as e: - logger.exception(e) - raise ActionFailedException( - f"Running action handler '{handler.name}' failed(Run {run_id}): {e}" - ) - - end_time = time.time_ns() - duration = (end_time - start_time) / 1_000_000 - logger.trace( - f"R{run_id} | End of execution of action handler {handler.name}" - f" on {str(payload)[:100]}..., duration: {duration}ms" - ) - return execution_result - - def reload_action(action_name: str) -> None: if global_state.runner_context is None: # TODO: raise error diff --git a/src/finecode/extension_runner/start.py b/src/finecode/extension_runner/start.py index 866009f..5be79ef 100644 --- a/src/finecode/extension_runner/start.py +++ b/src/finecode/extension_runner/start.py @@ -6,7 +6,6 @@ import finecode.extension_runner.global_state as global_state import finecode.extension_runner.lsp_server as extension_runner_lsp -import finecode.extension_runner.project_dirs as project_dirs from finecode import logs # import finecode.pygls_server_utils as pygls_server_utils @@ -55,15 +54,15 @@ def start_runner_sync(env_name: str) -> None: - project_log_dir_path = project_dirs.get_project_dir(global_state.project_dir_path) logger.remove() # disable logging raw messages # TODO: make configurable logger.configure(activation=[("pygls.protocol.json_rpc", False)]) # ~~extension runner communicates with workspace manager with tcp, we can print logs # to stdout as well~~. See README.md + assert global_state.project_dir_path is not None logs.save_logs_to_file( - file_path=project_log_dir_path / f"execution_{env_name}.log", + file_path=global_state.project_dir_path / '.venvs' / env_name / 'logs' / "runner.log", log_level=global_state.log_level, stdout=False, ) diff --git a/src/finecode/workspace_manager/cli.py b/src/finecode/workspace_manager/cli.py index 95b66f0..ca3c34d 100644 --- a/src/finecode/workspace_manager/cli.py +++ b/src/finecode/workspace_manager/cli.py @@ -11,6 +11,7 @@ from finecode.workspace_manager import logger_utils, user_messages from finecode.workspace_manager.cli_app import run as run_cmd from finecode.workspace_manager.cli_app import dump_config as dump_config_cmd +from finecode.workspace_manager.cli_app import prepare_envs as prepare_envs_cmd @click.group() @@ -31,6 +32,8 @@ def cli(): ... @click.option( "--port", "port", default=None, type=int, help="Port for TCP and WS server" ) +@click.option("--mcp", "mcp", is_flag=True, default=False) +@click.option("--mcp-port", "mcp_port", default=None, type=int, help="Port for MCP server") def start_api( trace: bool, debug: bool, @@ -39,6 +42,8 @@ def start_api( stdio: bool, host: str | None, port: int | None, + mcp: bool, + mcp_port: int | None ): if debug is True: import debugpy @@ -166,6 +171,34 @@ def run(ctx) -> None: except Exception as exception: logger.exception(exception) click.echo("Unexpected error, see logs in file for more details", err=True) + sys.exit(2) + + +@cli.command() +@click.option("--trace", "trace", is_flag=True, default=False) +@click.option("--debug", "debug", is_flag=True, default=False) +def prepare_envs(trace: bool, + debug: bool) -> None: + """ + `prepare-envs` should be called from workspace/project root directory. + """ + # idea: project parameter to allow to run from other directories? + if debug is True: + import debugpy + + try: + debugpy.listen(5680) + debugpy.wait_for_client() + except Exception as e: + logger.info(e) + + logger_utils.init_logger(trace=trace, stdout=True) + + try: + asyncio.run(prepare_envs_cmd.prepare_envs(workdir_path=pathlib.Path(os.getcwd()))) + except Exception as exception: # TODO + logger.exception(exception) + click.echo(exception, err=True) sys.exit(1) diff --git a/src/finecode/workspace_manager/cli_app/dump_config.py b/src/finecode/workspace_manager/cli_app/dump_config.py index 8e41e2d..5abf41e 100644 --- a/src/finecode/workspace_manager/cli_app/dump_config.py +++ b/src/finecode/workspace_manager/cli_app/dump_config.py @@ -43,14 +43,17 @@ async def dump_config(workdir_path: pathlib.Path, project_name: str): project_dir_path = list(ws_context.ws_projects.keys())[0] dump_dir_path = project_dir_path / 'finecode_config_dump' dump_file_path = dump_dir_path / 'pyproject.toml' - project_raw_config = ws_context.ws_projects_raw_configs[project_dir_path] - raw_config_str = dump_configs.dump_config(project_raw_config) - - os.makedirs(dump_dir_path, exist_ok=True) - with open(dump_file_path, 'w') as dump_file: - dump_file.write(raw_config_str) + project_def = ws_context.ws_projects[project_dir_path] + await services.run_action( + action_name='dump_config', + params={"source_file_path": project_def.def_path, "project_raw_config": project_raw_config, "target_file_path": dump_file_path }, + project_def=project_def, + ws_context=ws_context, + result_format=services.RunResultFormat.STRING, + preprocess_payload=False + ) logger.info(f"Dumped config into {dump_file_path}") finally: services.on_shutdown(ws_context) diff --git a/src/finecode/workspace_manager/cli_app/prepare_envs.py b/src/finecode/workspace_manager/cli_app/prepare_envs.py new file mode 100644 index 0000000..6c27f67 --- /dev/null +++ b/src/finecode/workspace_manager/cli_app/prepare_envs.py @@ -0,0 +1,104 @@ +import pathlib +import shutil +from loguru import logger + +from finecode.workspace_manager import context, services, domain +from finecode.workspace_manager.config import read_configs, collect_actions +from finecode.workspace_manager.cli_app import run as run_cli +from finecode.workspace_manager.runner import manager as runner_manager + + +async def prepare_envs(workdir_path: pathlib.Path) -> None: + # similar to `run_actions`, but with certain differences: + # - prepare_envs doesn't support presets because `dev_no_runtime` env most + # probably doesn't exist yet + # - we don't need to check missing actions, because prepare_envs is a builtin action + # and it exists always + ws_context = context.WorkspaceContext([workdir_path]) + await read_configs.read_projects_in_dir( + dir_path=workdir_path, ws_context=ws_context + ) + + # `prepare_envs` can be run only from workspace/project root. Validate this + if workdir_path not in ws_context.ws_projects: + # TODO: better exception + raise Exception("prepare_env can be run only from workspace/project root") + + projects = list(ws_context.ws_projects.values()) + + # Collect actions in relevant projects + for project in projects: + await read_configs.read_project_config(project=project, ws_context=ws_context, resolve_presets=False) + collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + + actions_by_projects: dict[pathlib.Path, list[str]] = {project.dir_path: ['prepare_envs'] for project in projects} + # action payload can be kept empty because it will be filled in payload preprocessor + action_payload: dict[str, str] = {} + + try: + # try to start runner in 'dev_workspace' env of each project. If venv doesn't + # exist or doesn't work, recreate it by running actions in the current env. + await start_or_recreate_all_dev_workspace_envs(projects=projects, ws_context=ws_context) + + # now all 'dev_workspace' envs are valid, run 'prepare_envs' in them to create + # envs in each subproject. + await run_cli.start_required_environments(actions_by_projects, ws_context) + + try: + await run_cli.run_actions_in_all_projects( + actions_by_projects, action_payload, ws_context, concurrently=True + ) + except run_cli.RunFailed as error: + logger.error(error.message) + finally: + services.on_shutdown(ws_context) + + +async def start_or_recreate_all_dev_workspace_envs(projects: list[domain.Project], ws_context: context.WorkspaceContext) -> None: + projects_dirs_with_invalid_envs: list[pathlib.Path] = [] + + for project in projects: + try: + runner = await runner_manager.start_runner( + project_def=project, + env_name='dev_workspace', + ws_context=ws_context + ) + except runner_manager.RunnerFailedToStart as e: + logger.warning(f"Failed to start runner for env 'dev_workspace' in project '{project.name}': {e}, recreate it") + projects_dirs_with_invalid_envs.append(project.dir_path) + + if len(projects_dirs_with_invalid_envs) > 0: + # to recreate dev_workspace env, run `prepare_envs` in runner of current project + current_project_dir_path = ws_context.ws_dirs_paths[0] + current_project = ws_context.ws_projects[current_project_dir_path] + try: + runner = await runner_manager.start_runner(project_def=current_project, env_name='dev_workspace', ws_context=ws_context) + except runner_manager.RunnerFailedToStart as exception: + # TODO + raise exception + + envs = [] + for project_dir_path in projects_dirs_with_invalid_envs: + # dependencies in `dev_workspace` should be simple and installable without + # dumping + envs.append({"name": "dev_workspace", "venv_dir_path": project_dir_path / '.venvs' / 'dev_workspace', "project_def_path": project_dir_path / 'pyproject.toml' }) + + # remove existing invalid envs + for env_info in envs: + if env_info.venv_dir_path.exists(): + logger.trace(f"{env_info.venv_dir_path} was invalid, remove it") + shutil.rmtree(env_info.venv_dir_path) + + try: + # TODO: check result + await services.run_action( + action_name='prepare_dev_workspaces_envs', + params={ "envs": envs, }, + project_def=current_project, + ws_context=ws_context, + result_format=services.RunResultFormat.STRING, + preprocess_payload=False + ) + finally: + runner_manager.stop_extension_runner_sync(runner) diff --git a/src/finecode/workspace_manager/cli_app/run.py b/src/finecode/workspace_manager/cli_app/run.py index 657d1bc..28cbde8 100644 --- a/src/finecode/workspace_manager/cli_app/run.py +++ b/src/finecode/workspace_manager/cli_app/run.py @@ -7,11 +7,49 @@ from loguru import logger from finecode.workspace_manager import context, domain, services -from finecode.workspace_manager.config import read_configs +from finecode.workspace_manager.config import read_configs, collect_actions from finecode.workspace_manager.runner import manager as runner_manager -class RunFailed(Exception): ... +class RunFailed(Exception): + def __init__(self, message: str) -> None: + self.message = message + + +async def start_required_environments( + actions_by_projects: dict[pathlib.Path, list[str]], + ws_context: context.WorkspaceContext +) -> None: + """Collect all required envs from actions that will be run and start them.""" + required_envs_by_project: dict[pathlib.Path, set[str]] = {} + for project_dir_path, action_names in actions_by_projects.items(): + project = ws_context.ws_projects[project_dir_path] + if project.actions is not None: + project_required_envs = set() + for action_name in action_names: + # find the action and collect envs from its handlers + action = next((a for a in project.actions if a.name == action_name), None) + if action is not None: + for handler in action.handlers: + project_required_envs.add(handler.env) + required_envs_by_project[project_dir_path] = project_required_envs + + # start runners for required environments that aren't already running + for project_dir_path, required_envs in required_envs_by_project.items(): + project = ws_context.ws_projects[project_dir_path] + existing_runners = ws_context.ws_projects_extension_runners.get(project_dir_path, {}) + + for env_name in required_envs: + if env_name not in existing_runners: + try: + runner = await runner_manager.start_runner( + project_def=project, + env_name=env_name, + ws_context=ws_context + ) + except Exception as e: + logger.warning(f"Failed to start runner for env '{env_name}' in project '{project.name}': {e}") + # TODO: raise error async def run_actions( @@ -35,20 +73,32 @@ async def run_actions( if project.name in projects_names } + projects: list[domain.Project] = [] + if projects_names is not None: + projects = get_projects_by_names( + projects_names, ws_context, workdir_path + ) + else: + projects = list(ws_context.ws_projects.values()) + try: + # 1. Start runners with presets to be able to resolve presets. Presets are + # required to be able to collect all actions, actions handlers and configs. try: - await runner_manager.update_runners(ws_context) + await runner_manager.start_runners_with_presets(projects, ws_context) except runner_manager.RunnerFailedToStart as exception: raise RunFailed( f"One or more projects are misconfigured, runners for them didn't" f" start: {exception.message}. Check logs for details." ) + # 2. Collect actions in relevant projects + for project in projects: + await read_configs.read_project_config(project=project, ws_context=ws_context) + collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + actions_by_projects: dict[pathlib.Path, list[str]] = {} if projects_names is not None: - projects: list[domain.Project] = get_projects_by_names( - projects_names, ws_context, workdir_path - ) # check that all projects have all actions to detect problem and provide # feedback as early as possible actions_set: ordered_set.OrderedSet[str] = ordered_set.OrderedSet(actions) @@ -67,6 +117,8 @@ async def run_actions( # actions will be run in all projects inside actions_by_projects = find_projects_with_actions(ws_context, actions) + await start_required_environments(actions_by_projects, ws_context) + return await run_actions_in_all_projects( actions_by_projects, action_payload, ws_context, concurrently ) @@ -215,8 +267,12 @@ async def run_actions_in_running_project( ) run_tasks.append(run_task) except ExceptionGroup as eg: - for error in eg.exceptions: - logger.exception(error) + for exception in eg.exceptions: + if isinstance(exception, services.ActionRunFailed): + logger.error(exception.message) + else: + logger.error("Unexpected exception:") + logger.exception(exception) raise RunFailed(f"Running of actions {actions} failed") for idx, run_task in enumerate(run_tasks): @@ -271,6 +327,7 @@ async def run_actions_in_all_projects( project_handler_tasks.append(project_task) except ExceptionGroup as eg: for exception in eg.exceptions: + # TODO: merge all in one? raise exception result_output: str = "" diff --git a/src/finecode/workspace_manager/config/read_configs.py b/src/finecode/workspace_manager/config/read_configs.py index 97b6432..3555701 100644 --- a/src/finecode/workspace_manager/config/read_configs.py +++ b/src/finecode/workspace_manager/config/read_configs.py @@ -1,6 +1,7 @@ from pathlib import Path from typing import Any, NamedTuple +import ordered_set from loguru import logger from tomlkit import loads as toml_loads @@ -54,7 +55,7 @@ async def read_projects_in_dir( async def read_project_config( - project: domain.Project, ws_context: context.WorkspaceContext + project: domain.Project, ws_context: context.WorkspaceContext, resolve_presets: bool = True ) -> None: # this function requires running project extension runner to get configuration # from it @@ -65,7 +66,7 @@ async def read_project_config( # TODO: validate that finecode is installed? finecode_raw_config = project_def.get("tool", {}).get("finecode", None) - if finecode_raw_config: + if finecode_raw_config and resolve_presets: finecode_config = config_models.FinecodeConfig(**finecode_raw_config) # all presets expected to be in `dev_no_runtime` environment project_runners = ws_context.ws_projects_extension_runners[project.dir_path] @@ -79,9 +80,46 @@ async def read_project_config( _merge_projects_configs(project_def, new_config) # add builtins if they are not overwritten - prepare_envs_action = domain.Action(name='prepare_envs', source='finecode_extension_api.actions.prepare_envs.PrepareEnvsAction', handlers=[domain.ActionHandler(name='prepare_venvs', source='fine_python_virtualenv.VirtualenvPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_venv==0.1.*'])], config={}) + prepare_envs_action = domain.Action( + name='prepare_envs', + source='finecode_extension_api.actions.prepare_envs.PrepareEnvsAction', + handlers=[ + domain.ActionHandler(name='prepare_envs_venvs', source='fine_python_virtualenv.VirtualenvPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_virtualenv==0.1.*']), + domain.ActionHandler(name='prepare_envs_dump_configs', source='finecode.extension_runner.action_handlers.PrepareEnvsDumpConfigsHandler', config={}, env='dev_workspace', dependencies=[]), + domain.ActionHandler(name='prepare_envs_pip', source='fine_python_pip.PipPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_pip==0.1.*']) + ], + config={} + ) add_action_to_config_if_new(project_def, prepare_envs_action) + # preparing dev workspaces doesn't need dumping config for two reasons: + # - depedencies in `dev_workspace` are expected to be simple and installable + # without dump + # - dumping is modifiable as action, so it can be correctly done only in + # dev_workspace env of the project and we just create it here, it doesn't + # exist yet + prepare_dev_workspaces_envs_action = domain.Action( + name='prepare_dev_workspaces_envs', + source='finecode_extension_api.actions.prepare_envs.PrepareEnvsAction', + handlers=[ + domain.ActionHandler(name='prepare_venvs', source='fine_python_virtualenv.VirtualenvPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_virtualenv==0.1.*']), + domain.ActionHandler(name='prepare_venvs_pip', source='fine_python_pip.PipPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_pip==0.1.*']) + ], + config={} + ) + add_action_to_config_if_new(project_def, prepare_dev_workspaces_envs_action) + + dump_config_action = domain.Action( + name='dump_config', + source='finecode_extension_api.actions.dump_config.DumpConfigAction', + handlers=[ + domain.ActionHandler(name='dump_config', source='finecode.extension_runner.action_handlers.DumpConfigHandler', config={}, env='dev_workspace', dependencies=[]), + domain.ActionHandler(name='dump_config_save', source='finecode.extension_runner.action_handlers.DumpConfigSaveHandler', config={}, env='dev_workspace', dependencies=[]) + ], + config={} + ) + add_action_to_config_if_new(project_def, dump_config_action) + # add runtime dependency group if it's not explicitly declared add_runtime_dependency_group_if_new(project_def) @@ -376,3 +414,11 @@ def merge_handlers_dependencies_into_groups(project_config: dict[str, Any]) -> N env_deps = deps_groups[handler_env] # should we remove duplicates here? env_deps += deps + + # remove duplicates in dependencies because multiple handlers can have the same + # dependencies / be from the same package + for group_name in deps_groups.keys(): + deps_list = deps_groups[group_name] + deps_set = ordered_set.OrderedSet(deps_list) + new_deps_list = list(deps_set) + deps_groups[group_name] = new_deps_list diff --git a/src/finecode/workspace_manager/payload_preprocessor.py b/src/finecode/workspace_manager/payload_preprocessor.py index f8e5dbd..dc2dd77 100644 --- a/src/finecode/workspace_manager/payload_preprocessor.py +++ b/src/finecode/workspace_manager/payload_preprocessor.py @@ -1,11 +1,12 @@ import pathlib import typing -from finecode.workspace_manager import project_analyzer +from finecode.workspace_manager import context, project_analyzer def preprocess_for_project( - action_name: str, payload: dict[str, typing.Any], project_dir_path: pathlib.Path + action_name: str, payload: dict[str, typing.Any], project_dir_path: pathlib.Path, + ws_context: context.WorkspaceContext, ) -> dict[str, typing.Any]: processed_payload = payload.copy() @@ -14,8 +15,27 @@ def preprocess_for_project( if action_name == "lint" or action_name == "format": if "file_paths" not in processed_payload: processed_payload["file_paths"] = None - if "save" not in processed_payload: + + if action_name == "format" and "save" not in processed_payload: processed_payload["save"] = True + elif action_name == "prepare_envs": + runtime_venv_path = project_dir_path / '.venvs' / 'runtime' + project_def_path = project_dir_path / 'pyproject.toml' + envs = [{"name": "runtime", "venv_dir_path": runtime_venv_path, "project_def_path": project_def_path}] + # current approach: there are 4 default environments: runtime, dev_workspace, + # dev, dev_no_runtime. `runtime` is created always, all other only if dependency + # group for them exist. + # In future there will be possibility to create additional envs and to configure + # default ones. + project_raw_config = ws_context.ws_projects_raw_configs[project_dir_path] + deps_groups = project_raw_config.get('dependency-groups', {}) + # `dev_workspace` is handled separately in `prepare_env`, no need to include + # here + for default_env in ['dev', 'dev_no_runtime']: + if default_env in deps_groups: + venv_path = project_dir_path / '.venvs' / default_env + envs.append({"name": default_env, "venv_dir_path": venv_path, "project_def_path": project_def_path }) + processed_payload["envs"] = envs for param, value in processed_payload.items(): if param == "file_paths" and value is None: diff --git a/src/finecode/workspace_manager/runner/manager.py b/src/finecode/workspace_manager/runner/manager.py index dce6d7b..d2b562a 100644 --- a/src/finecode/workspace_manager/runner/manager.py +++ b/src/finecode/workspace_manager/runner/manager.py @@ -127,6 +127,16 @@ async def on_progress(params: types.ProgressParams): register_progress_feature = runner_info_instance.client.feature(types.PROGRESS) register_progress_feature(on_progress) + + async def get_project_raw_config(params): + # assume raw config exists, because if runner is running, there is always a + # raw config + return { "config": json.dumps(ws_context.ws_projects_raw_configs[runner_dir]) } + + register_get_project_raw_config_feature = runner_info_instance.client.feature( + "projects/getRawConfig" + ) + register_get_project_raw_config_feature(get_project_raw_config) return runner_info_instance @@ -199,29 +209,19 @@ async def update_runners(ws_context: context.WorkspaceContext) -> None: await stop_extension_runner(runner) del ws_context.ws_projects_extension_runners[deleted_dir] + projects = [ws_context.ws_projects[new_dir] for new_dir in new_dirs] + # first start runners with presets to be able to resolve presets + await start_runners_with_presets(projects, ws_context) + new_runners_tasks: list[asyncio.Task] = [] try: - # first start runner in 'dev_no_runtime' env to be able to resolve presets for - # other envs - async with asyncio.TaskGroup() as tg: - for new_dir in new_dirs: - project = ws_context.ws_projects[new_dir] - project_status = project.status - if project_status == domain.ProjectStatus.CONFIG_VALID: - task = tg.create_task(_start_dev_no_runtime_runner(project_def=project, ws_context=ws_context)) - new_runners_tasks.append(task) - elif project_status != domain.ProjectStatus.NO_FINECODE: - raise RunnerFailedToStart(f"Project '{project.name}' has invalid configuration, status: {project_status.name}") - - save_runners_from_tasks_in_context(tasks=new_runners_tasks, ws_context=ws_context) - # only then start runners for all other envs new_runners_tasks = [] async with asyncio.TaskGroup() as tg: for new_dir in new_dirs: project = ws_context.ws_projects[new_dir] project_status = project.status - if ws_context.ws_projects_extension_runners.get(new_dir, {}).get('dev_no_runtime', None) is None: + if ws_context.ws_projects_extension_runners.get(new_dir, {}).get('dev_no_runtime', None) is not None: # start only if dev_no_runtime started successfully for env in project.envs: if env == 'dev_no_runtime': @@ -263,11 +263,35 @@ async def update_runners(ws_context: context.WorkspaceContext) -> None: raise RunnerFailedToStart("Failed to initialize runner") -async def _start_dev_no_runtime_runner(project_def: domain.Project, ws_context: context.WorkspaceContext) -> runner_info.ExtensionRunnerInfo: - runner = await start_extension_runner(runner_dir=project_def.dir_path, env_name='dev_no_runtime', ws_context=ws_context) +async def start_runners_with_presets(projects: list[domain.Project], ws_context: context.WorkspaceContext) -> None: + new_runners_tasks: list[asyncio.Task] = [] + try: + # first start runner in 'dev_no_runtime' env to be able to resolve presets for + # other envs (presets can be currently only in `dev_no_runtime` env) + async with asyncio.TaskGroup() as tg: + for project in projects: + project_status = project.status + if project_status == domain.ProjectStatus.CONFIG_VALID: + task = tg.create_task(_start_dev_no_runtime_runner(project_def=project, ws_context=ws_context)) + new_runners_tasks.append(task) + elif project_status != domain.ProjectStatus.NO_FINECODE: + raise RunnerFailedToStart(f"Project '{project.name}' has invalid configuration, status: {project_status.name}") + + save_runners_from_tasks_in_context(tasks=new_runners_tasks, ws_context=ws_context) + except ExceptionGroup as eg: + for exception in eg.exceptions: + if isinstance(exception, runner_client.BaseRunnerRequestException): + logger.error(exception.message) + else: + logger.exception(exception) + raise RunnerFailedToStart("Failed to initialize runner") + + +async def start_runner(project_def: domain.Project, env_name: str, ws_context: context.WorkspaceContext) -> runner_info.ExtensionRunnerInfo: + runner = await start_extension_runner(runner_dir=project_def.dir_path, env_name=env_name, ws_context=ws_context) if runner is None: - raise Exception("Runner failed to start") + raise RunnerFailedToStart("Runner failed to start") save_runner_in_context(runner=runner, ws_context=ws_context) @@ -276,18 +300,22 @@ async def _start_dev_no_runtime_runner(project_def: domain.Project, ws_context: # because this requires resolved project config with presets await _init_lsp_client(runner=runner, project=project_def) - - await read_configs.read_project_config(project=project_def, ws_context=ws_context) - collect_actions.collect_actions( - project_path=project_def.dir_path, ws_context=ws_context - ) - + if project_def.dir_path not in ws_context.ws_projects_raw_configs or project_def.actions is None: + await read_configs.read_project_config(project=project_def, ws_context=ws_context) + collect_actions.collect_actions( + project_path=project_def.dir_path, ws_context=ws_context + ) + await _update_runner_config(runner=runner, project=project_def) await _finish_runner_init(runner=runner, project=project_def, ws_context=ws_context) return runner +async def _start_dev_no_runtime_runner(project_def: domain.Project, ws_context: context.WorkspaceContext) -> runner_info.ExtensionRunnerInfo: + return await start_runner(project_def=project_def, env_name='dev_no_runtime', ws_context=ws_context) + + async def _init_runner( runner: runner_info.ExtensionRunnerInfo, project: domain.Project, From 9bfcbd485cd5df69d821e6e80c64029e39813f40 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Tue, 12 Aug 2025 14:46:59 +0200 Subject: [PATCH 04/46] Fix filtering duplicates in dependencies. Change package structure in finecode_extension_api from flat to src. Prepare envs only in packages which use finecode, not just all. --- .../finecode_extension_api/__init__.py | 0 .../actions/__init__.py | 0 .../finecode_extension_api/actions/build.py | 64 +++++++++++++++++++ .../actions/check_formatting.py} | 0 .../actions/dump_config.py | 0 .../finecode_extension_api/actions/format.py | 0 .../actions/ide}/__init__.py | 0 .../actions/ide/text_document_code_action.py | 0 .../actions/ide/text_document_inlay_hint.py | 0 .../finecode_extension_api/actions/lint.py | 0 .../actions/prepare_envs.py | 0 .../finecode_extension_api/code_action.py | 0 .../finecode_extension_api/common_types.py | 0 .../interfaces/__init__.py} | 0 .../interfaces/iactionrunner.py | 0 .../interfaces/icache.py | 0 .../interfaces/icommandrunner.py | 0 .../interfaces/ifilemanager.py | 0 .../interfaces/ilogger.py | 0 .../interfaces/iprocessexecutor.py | 0 .../interfaces/iprojectinfoprovider.py | 0 .../partialresultscheduler.py | 0 .../src/finecode_extension_api/py.typed | 0 .../finecode_extension_api/textstyler.py | 0 .../workspace_manager/cli_app/prepare_envs.py | 13 ++-- .../workspace_manager/config/read_configs.py | 14 ++-- 26 files changed, 83 insertions(+), 8 deletions(-) rename finecode_extension_api/{ => src}/finecode_extension_api/__init__.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/actions/__init__.py (100%) create mode 100644 finecode_extension_api/src/finecode_extension_api/actions/build.py rename finecode_extension_api/{finecode_extension_api/actions/ide/__init__.py => src/finecode_extension_api/actions/check_formatting.py} (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/actions/dump_config.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/actions/format.py (100%) rename finecode_extension_api/{finecode_extension_api/interfaces => src/finecode_extension_api/actions/ide}/__init__.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/actions/ide/text_document_code_action.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/actions/ide/text_document_inlay_hint.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/actions/lint.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/actions/prepare_envs.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/code_action.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/common_types.py (100%) rename finecode_extension_api/{finecode_extension_api/py.typed => src/finecode_extension_api/interfaces/__init__.py} (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/interfaces/iactionrunner.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/interfaces/icache.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/interfaces/icommandrunner.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/interfaces/ifilemanager.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/interfaces/ilogger.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/interfaces/iprocessexecutor.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/interfaces/iprojectinfoprovider.py (100%) rename finecode_extension_api/{ => src}/finecode_extension_api/partialresultscheduler.py (100%) create mode 100644 finecode_extension_api/src/finecode_extension_api/py.typed rename finecode_extension_api/{ => src}/finecode_extension_api/textstyler.py (100%) diff --git a/finecode_extension_api/finecode_extension_api/__init__.py b/finecode_extension_api/src/finecode_extension_api/__init__.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/__init__.py rename to finecode_extension_api/src/finecode_extension_api/__init__.py diff --git a/finecode_extension_api/finecode_extension_api/actions/__init__.py b/finecode_extension_api/src/finecode_extension_api/actions/__init__.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/actions/__init__.py rename to finecode_extension_api/src/finecode_extension_api/actions/__init__.py diff --git a/finecode_extension_api/src/finecode_extension_api/actions/build.py b/finecode_extension_api/src/finecode_extension_api/actions/build.py new file mode 100644 index 0000000..7f0d219 --- /dev/null +++ b/finecode_extension_api/src/finecode_extension_api/actions/build.py @@ -0,0 +1,64 @@ +import dataclasses +import pathlib +import sys +import typing + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +from finecode_extension_api import code_action, textstyler + + +@dataclasses.dataclass +class BuildRunPayload(code_action.RunActionPayload): + # path to package root dir, where usually pyproject.toml is located. Note, that + # packages can have different layouts. Use service (TODO) to get package source + # directory path and avoid need to handle all possible cases by yourself. + package_root_path: pathlib.Path + build_type: typing.Literal['release'] | typing.Literal['debug'] = 'release' + # TODO: entrypoint + # TODO: package type + # TODO: target platform? (including version etc) + + +class BuildRunContext(code_action.RunActionContext): + def __init__( + self, + run_id: int, + ) -> None: + super().__init__(run_id=run_id) + + +@dataclasses.dataclass +class BuildRunResult(code_action.RunActionResult): + # files/directories which are results of build + # TODO: for better abstraction we could split build and packaging even in case of + # wheel and sdist + results: list[pathlib.Path] + + @override + def update(self, other: code_action.RunActionResult) -> None: + if not isinstance(other, BuildRunResult): + return + + def to_text(self) -> str | textstyler.StyledText: + return '' + + +# general build action: any type of project should be built: library(pure and not pure python), application(both pure distributed as python package and application transformed to executable) +# concrete use cases: +# 1. Pure Python package built with `build` and `setuptools`, result: sdist and wheel. +# 2. Python Package with mypyc. TODO +# 3. Application distributed as python package: the same as use case 1. +# 4. Application compiled with Nuitka to executable. +# 5. Application packaged to executable with pyinstaller or similar tool. +# +# Customization examples: +# - Recognize constructs(syntax, imports) supported only by higher versions of python and replace them by alternatives from older python. One universal wheel will become version-specific wheel. +# - the same could be applied for platform-specific functionalities +# - optimize implementation +type BuildAction = code_action.Action[ + BuildRunPayload, BuildRunContext, BuildRunResult +] diff --git a/finecode_extension_api/finecode_extension_api/actions/ide/__init__.py b/finecode_extension_api/src/finecode_extension_api/actions/check_formatting.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/actions/ide/__init__.py rename to finecode_extension_api/src/finecode_extension_api/actions/check_formatting.py diff --git a/finecode_extension_api/finecode_extension_api/actions/dump_config.py b/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/actions/dump_config.py rename to finecode_extension_api/src/finecode_extension_api/actions/dump_config.py diff --git a/finecode_extension_api/finecode_extension_api/actions/format.py b/finecode_extension_api/src/finecode_extension_api/actions/format.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/actions/format.py rename to finecode_extension_api/src/finecode_extension_api/actions/format.py diff --git a/finecode_extension_api/finecode_extension_api/interfaces/__init__.py b/finecode_extension_api/src/finecode_extension_api/actions/ide/__init__.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/interfaces/__init__.py rename to finecode_extension_api/src/finecode_extension_api/actions/ide/__init__.py diff --git a/finecode_extension_api/finecode_extension_api/actions/ide/text_document_code_action.py b/finecode_extension_api/src/finecode_extension_api/actions/ide/text_document_code_action.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/actions/ide/text_document_code_action.py rename to finecode_extension_api/src/finecode_extension_api/actions/ide/text_document_code_action.py diff --git a/finecode_extension_api/finecode_extension_api/actions/ide/text_document_inlay_hint.py b/finecode_extension_api/src/finecode_extension_api/actions/ide/text_document_inlay_hint.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/actions/ide/text_document_inlay_hint.py rename to finecode_extension_api/src/finecode_extension_api/actions/ide/text_document_inlay_hint.py diff --git a/finecode_extension_api/finecode_extension_api/actions/lint.py b/finecode_extension_api/src/finecode_extension_api/actions/lint.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/actions/lint.py rename to finecode_extension_api/src/finecode_extension_api/actions/lint.py diff --git a/finecode_extension_api/finecode_extension_api/actions/prepare_envs.py b/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/actions/prepare_envs.py rename to finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py diff --git a/finecode_extension_api/finecode_extension_api/code_action.py b/finecode_extension_api/src/finecode_extension_api/code_action.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/code_action.py rename to finecode_extension_api/src/finecode_extension_api/code_action.py diff --git a/finecode_extension_api/finecode_extension_api/common_types.py b/finecode_extension_api/src/finecode_extension_api/common_types.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/common_types.py rename to finecode_extension_api/src/finecode_extension_api/common_types.py diff --git a/finecode_extension_api/finecode_extension_api/py.typed b/finecode_extension_api/src/finecode_extension_api/interfaces/__init__.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/py.typed rename to finecode_extension_api/src/finecode_extension_api/interfaces/__init__.py diff --git a/finecode_extension_api/finecode_extension_api/interfaces/iactionrunner.py b/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/interfaces/iactionrunner.py rename to finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py diff --git a/finecode_extension_api/finecode_extension_api/interfaces/icache.py b/finecode_extension_api/src/finecode_extension_api/interfaces/icache.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/interfaces/icache.py rename to finecode_extension_api/src/finecode_extension_api/interfaces/icache.py diff --git a/finecode_extension_api/finecode_extension_api/interfaces/icommandrunner.py b/finecode_extension_api/src/finecode_extension_api/interfaces/icommandrunner.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/interfaces/icommandrunner.py rename to finecode_extension_api/src/finecode_extension_api/interfaces/icommandrunner.py diff --git a/finecode_extension_api/finecode_extension_api/interfaces/ifilemanager.py b/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/interfaces/ifilemanager.py rename to finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py diff --git a/finecode_extension_api/finecode_extension_api/interfaces/ilogger.py b/finecode_extension_api/src/finecode_extension_api/interfaces/ilogger.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/interfaces/ilogger.py rename to finecode_extension_api/src/finecode_extension_api/interfaces/ilogger.py diff --git a/finecode_extension_api/finecode_extension_api/interfaces/iprocessexecutor.py b/finecode_extension_api/src/finecode_extension_api/interfaces/iprocessexecutor.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/interfaces/iprocessexecutor.py rename to finecode_extension_api/src/finecode_extension_api/interfaces/iprocessexecutor.py diff --git a/finecode_extension_api/finecode_extension_api/interfaces/iprojectinfoprovider.py b/finecode_extension_api/src/finecode_extension_api/interfaces/iprojectinfoprovider.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/interfaces/iprojectinfoprovider.py rename to finecode_extension_api/src/finecode_extension_api/interfaces/iprojectinfoprovider.py diff --git a/finecode_extension_api/finecode_extension_api/partialresultscheduler.py b/finecode_extension_api/src/finecode_extension_api/partialresultscheduler.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/partialresultscheduler.py rename to finecode_extension_api/src/finecode_extension_api/partialresultscheduler.py diff --git a/finecode_extension_api/src/finecode_extension_api/py.typed b/finecode_extension_api/src/finecode_extension_api/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/finecode_extension_api/finecode_extension_api/textstyler.py b/finecode_extension_api/src/finecode_extension_api/textstyler.py similarity index 100% rename from finecode_extension_api/finecode_extension_api/textstyler.py rename to finecode_extension_api/src/finecode_extension_api/textstyler.py diff --git a/src/finecode/workspace_manager/cli_app/prepare_envs.py b/src/finecode/workspace_manager/cli_app/prepare_envs.py index 6c27f67..534381b 100644 --- a/src/finecode/workspace_manager/cli_app/prepare_envs.py +++ b/src/finecode/workspace_manager/cli_app/prepare_envs.py @@ -24,7 +24,12 @@ async def prepare_envs(workdir_path: pathlib.Path) -> None: # TODO: better exception raise Exception("prepare_env can be run only from workspace/project root") - projects = list(ws_context.ws_projects.values()) + invalid_projects = [project for project in ws_context.ws_projects.values() if project.status == domain.ProjectStatus.CONFIG_INVALID] + if len(invalid_projects) > 0: + raise Exception(f"Projects have invalid configuration: {invalid_projects}") + + # prepare envs only in projects with valid configurations and which use finecode + projects = [project for project in ws_context.ws_projects.values() if project.status == domain.ProjectStatus.CONFIG_VALID] # Collect actions in relevant projects for project in projects: @@ -86,9 +91,9 @@ async def start_or_recreate_all_dev_workspace_envs(projects: list[domain.Project # remove existing invalid envs for env_info in envs: - if env_info.venv_dir_path.exists(): - logger.trace(f"{env_info.venv_dir_path} was invalid, remove it") - shutil.rmtree(env_info.venv_dir_path) + if env_info["venv_dir_path"].exists(): + logger.trace(f"{env_info['venv_dir_path']} was invalid, remove it") + shutil.rmtree(env_info["venv_dir_path"]) try: # TODO: check result diff --git a/src/finecode/workspace_manager/config/read_configs.py b/src/finecode/workspace_manager/config/read_configs.py index 3555701..eced811 100644 --- a/src/finecode/workspace_manager/config/read_configs.py +++ b/src/finecode/workspace_manager/config/read_configs.py @@ -1,7 +1,6 @@ from pathlib import Path from typing import Any, NamedTuple -import ordered_set from loguru import logger from tomlkit import loads as toml_loads @@ -419,6 +418,13 @@ def merge_handlers_dependencies_into_groups(project_config: dict[str, Any]) -> N # dependencies / be from the same package for group_name in deps_groups.keys(): deps_list = deps_groups[group_name] - deps_set = ordered_set.OrderedSet(deps_list) - new_deps_list = list(deps_set) - deps_groups[group_name] = new_deps_list + + # another possibility would be to use `ordered_set.OrderedSet`, but dependency + # list can contain not only strings, but also dictionaries like + # `{ 'include-group': 'runtime' }` which are not hashable + unique_deps = [] + for dep in deps_list: + if dep not in unique_deps: + unique_deps.append(dep) + + deps_groups[group_name] = unique_deps From b37a87c8e215396193f6a2b9811a7b33e1bc8013 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 14 Aug 2025 12:52:45 +0200 Subject: [PATCH 05/46] Fix saving dumped config if target dir doesn't exist. Add create_dir method in ifilemanager. Install dependency in dev_workspace env also if they exist already --- .python-version | 1 - README.md | 6 +- extensions/fine_python_pip/pyproject.toml | 2 +- .../fine_python_virtualenv/pyproject.toml | 2 +- .../interfaces/ifilemanager.py | 3 + poetry.toml | 2 - pyproject.toml | 22 ++-- .../action_handlers/dump_config_save.py | 2 + src/finecode/extension_runner/cli.py | 18 +++- .../extension_runner/impls/file_manager.py | 4 + .../workspace_manager/cli_app/prepare_envs.py | 100 +++++++++++------- .../workspace_manager/runner/manager.py | 36 ++++++- 12 files changed, 138 insertions(+), 60 deletions(-) delete mode 100644 .python-version delete mode 100644 poetry.toml diff --git a/.python-version b/.python-version deleted file mode 100644 index 2c07333..0000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.11 diff --git a/README.md b/README.md index ec0832b..bfb86e3 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ With FineCode you can: dev_workspace = ["finecode==0.2.*"] ``` -1.1.2 Create dev_workspace venv: `python -m venv .venvs/dev_workspace` (https://docs.python.org/3/library/venv.html#creating-virtual-environments ) +1.1.2 Create dev_workspace venv: `python -m venv .venvs/dev_workspace` ([Python Docs](https://docs.python.org/3/library/venv.html#creating-virtual-environments )) 1.1.3 Activate this venv and install dependencies from `dev_workspace` group: ``` @@ -45,10 +45,10 @@ NOTE: `pip install` supports `--group` parameter since pip 25.1. Make sure you h For list of presets from FineCode authors see 'Presets' section below. -1.2.1 Run `prepare_env` finecode action: +1.2.1 Run `prepare_envs` finecode command: ```bash - python -m finecode run prepare_env + python -m finecode prepare_envs ``` 1.3 Enable finecode and preset diff --git a/extensions/fine_python_pip/pyproject.toml b/extensions/fine_python_pip/pyproject.toml index 8f22ddc..e534e68 100644 --- a/extensions/fine_python_pip/pyproject.toml +++ b/extensions/fine_python_pip/pyproject.toml @@ -8,7 +8,7 @@ requires-python = ">=3.11, < 3.14" dependencies = ["finecode_extension_api==0.1.0"] [dependency-groups] -dev_workspace = ["finecode==0.2.0"] +dev_workspace = ["finecode==0.2.*"] dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] [tool.finecode] diff --git a/extensions/fine_python_virtualenv/pyproject.toml b/extensions/fine_python_virtualenv/pyproject.toml index 08f8cbb..ef3e83d 100644 --- a/extensions/fine_python_virtualenv/pyproject.toml +++ b/extensions/fine_python_virtualenv/pyproject.toml @@ -11,7 +11,7 @@ dependencies = [ ] [dependency-groups] -dev_workspace = ["finecode==0.2.0"] +dev_workspace = ["finecode==0.2.*"] dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] [tool.finecode] diff --git a/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py b/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py index 6334f13..95635d7 100644 --- a/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py +++ b/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py @@ -10,3 +10,6 @@ async def get_file_version(self, file_path: Path) -> str: ... async def save_file(self, file_path: Path, file_content) -> None: ... + + async def create_dir(self, dir_path: Path, create_parents: bool = True, exist_ok: bool = True): + ... \ No newline at end of file diff --git a/poetry.toml b/poetry.toml deleted file mode 100644 index d11b90b..0000000 --- a/poetry.toml +++ /dev/null @@ -1,2 +0,0 @@ -[virtualenvs] -prefer-active-python = true diff --git a/pyproject.toml b/pyproject.toml index 6bed5d7..9cd9e01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,6 @@ [project] name = "finecode" -version = "0.2.0" -# dynamic version from setuptools-scm is not compatible with poetry, because it requires explicit version in pyproject.toml. Will be used after migration from poetry. -# dynamic = ["version"] +dynamic = ["version"] description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" @@ -17,10 +15,17 @@ dependencies = [ "pygls==2.0.0-a2", "finecode_extension_api==0.1.0", "ordered-set==4.1.*", + "mcp==1.9.*", + "fine_python_virtualenv @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_virtualenv", + "fine_python_pip @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_pip", ] [dependency-groups] -dev_workspace = ["build==1.2.2.post1"] +dev_workspace = ["build==1.2.2.post1", "finecode==0.2.*"] +dev = [{ include-group = "runtime" }, "pytest==7.4.*", "debugpy==1.8.*"] +dev_no_runtime = [ + "fine_python_import_linter @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_import_linter", +] [build-system] requires = ["setuptools>=64", "setuptools-scm>=8"] @@ -29,13 +34,8 @@ build-backend = "setuptools.build_meta" [tool.poetry] packages = [{ include = "finecode", from = "src" }] -[tool.poetry.group.dev.dependencies] -pytest = "^7.4.3" -finecode_dev_common_preset = { path = "./finecode_dev_common_preset", develop = true } -fine_python_import_linter = { git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_import_linter" } -anyio = "^4.4.0" -debugpy = "^1.8.9" - +# TODO: intall finecode_dev_common_preset as a dependency from local path using finecode? +# TODO: add import linter handler to lint action [tool.finecode] presets = [{ source = "finecode_dev_common_preset" }] diff --git a/src/finecode/extension_runner/action_handlers/dump_config_save.py b/src/finecode/extension_runner/action_handlers/dump_config_save.py index b766a95..cca5eb1 100644 --- a/src/finecode/extension_runner/action_handlers/dump_config_save.py +++ b/src/finecode/extension_runner/action_handlers/dump_config_save.py @@ -24,7 +24,9 @@ async def run( self, payload: dump_config_action.DumpConfigRunPayload, run_context: dump_config_action.DumpConfigRunContext ) -> dump_config_action.DumpConfigRunResult: raw_config_str = tomlkit.dumps(run_context.raw_config_dump) + target_file_dir_path = payload.target_file_path.parent + await self.file_manager.create_dir(dir_path=target_file_dir_path) await self.file_manager.save_file( file_path=payload.target_file_path, file_content=raw_config_str ) diff --git a/src/finecode/extension_runner/cli.py b/src/finecode/extension_runner/cli.py index d6e6674..c6d19b2 100644 --- a/src/finecode/extension_runner/cli.py +++ b/src/finecode/extension_runner/cli.py @@ -1,5 +1,6 @@ import os from pathlib import Path +from importlib import metadata import click from loguru import logger @@ -8,7 +9,13 @@ from finecode.extension_runner import global_state -@click.command() +@click.group() +def main(): + """FineCode Extension Runner CLI""" + pass + + +@main.command() @click.option("--trace", "trace", is_flag=True, default=False) @click.option("--debug", "debug", is_flag=True, default=False) @click.option("--debug-port", "debug_port", type=int, default=5680) @@ -19,7 +26,7 @@ required=True, ) @click.option("--env-name", "env_name", type=str, default="unknown") -def main(trace: bool, debug: bool, debug_port: int, project_path: Path, env_name: str): +def start(trace: bool, debug: bool, debug_port: int, project_path: Path, env_name: str): if debug is True: import debugpy @@ -40,5 +47,12 @@ def main(trace: bool, debug: bool, debug_port: int, project_path: Path, env_name runner_start.start_runner_sync(env_name) +@main.command() +def version(): + """Show version information""" + package_version = metadata.version('finecode') + click.echo(f'FineCode Extension Runner {package_version}') + + if __name__ == "__main__": main() diff --git a/src/finecode/extension_runner/impls/file_manager.py b/src/finecode/extension_runner/impls/file_manager.py index e9d4883..0d7e2eb 100644 --- a/src/finecode/extension_runner/impls/file_manager.py +++ b/src/finecode/extension_runner/impls/file_manager.py @@ -75,6 +75,10 @@ async def save_file(self, file_path: Path, file_content: str) -> None: with open(file_path, "w") as f: f.write(file_content) + async def create_dir(self, dir_path: Path, create_parents: bool = True, exist_ok: bool = True): + # currently only local file system is supported + dir_path.mkdir(parents=create_parents, exist_ok=exist_ok) + # helper methods def read_content_file_from_fs(self, file_path: Path) -> str: # don't use this method directly, use `get_content` instead diff --git a/src/finecode/workspace_manager/cli_app/prepare_envs.py b/src/finecode/workspace_manager/cli_app/prepare_envs.py index 534381b..7d4ec8f 100644 --- a/src/finecode/workspace_manager/cli_app/prepare_envs.py +++ b/src/finecode/workspace_manager/cli_app/prepare_envs.py @@ -43,7 +43,7 @@ async def prepare_envs(workdir_path: pathlib.Path) -> None: try: # try to start runner in 'dev_workspace' env of each project. If venv doesn't # exist or doesn't work, recreate it by running actions in the current env. - await start_or_recreate_all_dev_workspace_envs(projects=projects, ws_context=ws_context) + await start_or_recreate_all_dev_workspace_envs(projects=projects, workdir_path=workdir_path, ws_context=ws_context) # now all 'dev_workspace' envs are valid, run 'prepare_envs' in them to create # envs in each subproject. @@ -59,51 +59,75 @@ async def prepare_envs(workdir_path: pathlib.Path) -> None: services.on_shutdown(ws_context) -async def start_or_recreate_all_dev_workspace_envs(projects: list[domain.Project], ws_context: context.WorkspaceContext) -> None: +async def start_or_recreate_all_dev_workspace_envs(projects: list[domain.Project], workdir_path: pathlib.Path, ws_context: context.WorkspaceContext) -> None: + projects_dirs_with_valid_envs: list[pathlib.Path] = [] projects_dirs_with_invalid_envs: list[pathlib.Path] = [] for project in projects: - try: - runner = await runner_manager.start_runner( - project_def=project, - env_name='dev_workspace', - ws_context=ws_context - ) - except runner_manager.RunnerFailedToStart as e: - logger.warning(f"Failed to start runner for env 'dev_workspace' in project '{project.name}': {e}, recreate it") + if project.dir_path == workdir_path: + # skip checking `dev_workspace` env of the current project, because user + # is responsible for keeping it correct + continue + + runner_is_valid = await runner_manager.check_runner( + runner_dir=project.dir_path, + env_name='dev_workspace' + ) + if runner_is_valid: + projects_dirs_with_valid_envs.append(project.dir_path) + else: + logger.warning(f"Runner for env 'dev_workspace' in project '{project.name}' is invalid, recreate it") projects_dirs_with_invalid_envs.append(project.dir_path) - if len(projects_dirs_with_invalid_envs) > 0: - # to recreate dev_workspace env, run `prepare_envs` in runner of current project - current_project_dir_path = ws_context.ws_dirs_paths[0] - current_project = ws_context.ws_projects[current_project_dir_path] - try: - runner = await runner_manager.start_runner(project_def=current_project, env_name='dev_workspace', ws_context=ws_context) - except runner_manager.RunnerFailedToStart as exception: - # TODO - raise exception + # to recreate dev_workspace env, run `prepare_envs` in runner of current project + current_project_dir_path = ws_context.ws_dirs_paths[0] + current_project = ws_context.ws_projects[current_project_dir_path] + try: + runner = await runner_manager.start_runner(project_def=current_project, env_name='dev_workspace', ws_context=ws_context) + except runner_manager.RunnerFailedToStart as exception: + # TODO + raise exception + try: envs = [] - for project_dir_path in projects_dirs_with_invalid_envs: + + # run pip install in dev_workspace even if env exists to make sure that correct + # dependencies are installed + for project_dir_path in projects_dirs_with_valid_envs: + if project_dir_path == workdir_path: + # skip installation of dependencies in `dev_workspace` env of the + # current project, because user is responsible for keeping them + # up-to-date + continue + # dependencies in `dev_workspace` should be simple and installable without # dumping envs.append({"name": "dev_workspace", "venv_dir_path": project_dir_path / '.venvs' / 'dev_workspace', "project_def_path": project_dir_path / 'pyproject.toml' }) - - # remove existing invalid envs - for env_info in envs: - if env_info["venv_dir_path"].exists(): - logger.trace(f"{env_info['venv_dir_path']} was invalid, remove it") - shutil.rmtree(env_info["venv_dir_path"]) - try: - # TODO: check result - await services.run_action( - action_name='prepare_dev_workspaces_envs', - params={ "envs": envs, }, - project_def=current_project, - ws_context=ws_context, - result_format=services.RunResultFormat.STRING, - preprocess_payload=False - ) - finally: - runner_manager.stop_extension_runner_sync(runner) + if len(projects_dirs_with_invalid_envs) > 0: + invalid_envs = [] + + for project_dir_path in projects_dirs_with_invalid_envs: + # dependencies in `dev_workspace` should be simple and installable without + # dumping + invalid_envs.append({"name": "dev_workspace", "venv_dir_path": project_dir_path / '.venvs' / 'dev_workspace', "project_def_path": project_dir_path / 'pyproject.toml' }) + + # remove existing invalid envs + for env_info in invalid_envs: + if env_info["venv_dir_path"].exists(): + logger.trace(f"{env_info['venv_dir_path']} was invalid, remove it") + shutil.rmtree(env_info["venv_dir_path"]) + + envs += invalid_envs + + # TODO: check result + await services.run_action( + action_name='prepare_dev_workspaces_envs', + params={ "envs": envs, }, + project_def=current_project, + ws_context=ws_context, + result_format=services.RunResultFormat.STRING, + preprocess_payload=False + ) + finally: + runner_manager.stop_extension_runner_sync(runner) diff --git a/src/finecode/workspace_manager/runner/manager.py b/src/finecode/workspace_manager/runner/manager.py index d2b562a..767d9b9 100644 --- a/src/finecode/workspace_manager/runner/manager.py +++ b/src/finecode/workspace_manager/runner/manager.py @@ -94,7 +94,7 @@ async def start_extension_runner( process_args_str: str = " ".join(process_args) client = await create_lsp_client_io( runner_info.CustomJsonRpcClient, - f"{python_cmd} -m finecode.extension_runner.cli {process_args_str}", + f"{python_cmd} -m finecode.extension_runner.cli start {process_args_str}", runner_dir, ) runner_info_instance.client = client @@ -427,3 +427,37 @@ async def send_opened_files( ) except ExceptionGroup as eg: logger.error(f"Error while sending opened document: {eg.exceptions}") + + +async def check_runner(runner_dir: Path, env_name: str) -> bool: + try: + python_cmd = finecode_cmd.get_python_cmd(runner_dir, env_name) + except ValueError: + logger.debug(f"No venv for {env_name} of {runner_dir}") + # no venv + return False + + # get version of extension runner. If it works and we get valid + # value, assume extension runner works correctly + cmd = f'{python_cmd} -m finecode.extension_runner.cli version' + logger.debug(f"Run '{cmd}' in {runner_dir}") + async_subprocess = await asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + cwd=runner_dir, + ) + try: + raw_stdout, raw_stderr = await asyncio.wait_for( + async_subprocess.communicate(), timeout=5 + ) + except asyncio.TimeoutError: + logger.debug(f"Timeout 5 sec({runner_dir})") + return False + + if async_subprocess.returncode != 0: + logger.debug(f"Return code: {async_subprocess.returncode}, stderr: {raw_stderr.decode()}") + return False + + stdout = raw_stdout.decode() + return 'FineCode Extension Runner ' in stdout From fb7e12beb1ff97fbfcd769f136606c6053a0fd98 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Fri, 15 Aug 2025 08:22:40 +0200 Subject: [PATCH 06/46] Move extension runner in separate package. Automatically add it to all group dependencies --- .gitignore | 7 ++- finecode_extension_api/.gitignore | 5 ++ finecode_extension_runner/.gitignore | 14 +++++ finecode_extension_runner/README.md | 5 ++ finecode_extension_runner/pyproject.toml | 61 +++++++++++++++++++ .../src/finecode_extension_runner/README.md | 1 + .../finecode_extension_runner}/__init__.py | 0 .../src/finecode_extension_runner/__main__.py | 4 ++ .../_services/__init__.py | 0 .../_services/run_action.py | 20 +++--- .../action_handlers/__init__.py | 0 .../action_handlers/dump_config.py | 0 .../action_handlers/dump_config_save.py | 0 .../prepare_envs_dump_configs.py | 0 .../action_utils.py | 0 .../src/finecode_extension_runner}/api.proto | 2 +- .../finecode_extension_runner}/app_dirs.py | 0 .../src/finecode_extension_runner}/cli.py | 6 +- .../src/finecode_extension_runner}/context.py | 2 +- .../finecode_extension_runner}/di/__init__.py | 0 .../finecode_extension_runner}/di/_state.py | 0 .../di/bootstrap.py | 8 +-- .../finecode_extension_runner}/di/resolver.py | 0 .../src/finecode_extension_runner}/domain.py | 2 +- .../global_state.py | 2 +- .../impls/__init__.py | 0 .../impls/action_runner.py | 0 .../impls/command_runner.py | 0 .../impls/file_manager.py | 11 ++-- .../impls/inmemory_cache.py | 0 .../impls/loguru_logger.py | 2 +- .../impls/process_executor.py | 0 .../impls/project_info_provider.py | 0 .../src/finecode_extension_runner}/logs.py | 7 +++ .../finecode_extension_runner}/lsp_server.py | 6 +- .../partial_result_sender.py | 0 .../project_dirs.py | 2 +- .../src/finecode_extension_runner/py.typed | 0 .../finecode_extension_runner}/run_utils.py | 0 .../src/finecode_extension_runner}/schemas.py | 0 .../finecode_extension_runner}/services.py | 10 +-- .../src/finecode_extension_runner}/start.py | 10 +-- .../utils}/__init__.py | 0 src/finecode/__main__.py | 2 +- .../{workspace_manager => }/app_dirs.py | 0 src/finecode/{workspace_manager => }/cli.py | 10 +-- .../config => cli_app}/__init__.py | 0 .../cli_app/dump_config.py | 6 +- .../cli_app/prepare_envs.py | 8 +-- .../{workspace_manager => }/cli_app/run.py | 6 +- .../lsp_server => config}/__init__.py | 0 .../config/collect_actions.py | 6 +- .../config/config_models.py | 0 .../config/dump_configs.py | 0 .../config/read_configs.py | 31 ++++++++-- .../{workspace_manager => }/context.py | 4 +- .../{workspace_manager => }/domain.py | 0 src/finecode/extension_runner/README.md | 15 ----- .../{workspace_manager => }/find_project.py | 4 +- .../{workspace_manager => }/finecode_cmd.py | 0 .../{workspace_manager => }/logger_utils.py | 4 +- .../endpoints => lsp_server}/__init__.py | 0 .../lsp_server/api.proto | 2 +- .../endpoints}/__init__.py | 0 .../lsp_server/endpoints/action_tree.py | 12 ++-- .../lsp_server/endpoints/code_actions.py | 2 +- .../lsp_server/endpoints/code_lens.py | 0 .../lsp_server/endpoints/diagnostics.py | 4 +- .../lsp_server/endpoints/document_sync.py | 6 +- .../lsp_server/endpoints/formatting.py | 4 +- .../lsp_server/endpoints/inlay_hints.py | 4 +- .../lsp_server/global_state.py | 2 +- .../lsp_server/lsp_server.py | 18 +++--- .../lsp_server/schemas.py | 0 .../lsp_server/services.py | 8 +-- src/finecode/{workspace_manager => }/main.py | 4 +- src/finecode/mcp_server.py | 43 +++++++++++++ .../payload_preprocessor.py | 2 +- .../project_analyzer.py | 0 .../{workspace_manager => }/proxy_utils.py | 8 +-- .../utils => runner}/__init__.py | 0 .../{workspace_manager => }/runner/manager.py | 12 ++-- .../runner/runner_client.py | 7 ++- .../runner/runner_info.py | 1 + .../{workspace_manager => }/services.py | 6 +- .../{workspace_manager => }/user_messages.py | 0 src/finecode/utils/__init__.py | 0 .../utils/async_proc_queue.py | 0 .../utils/iterable_subscribe.py | 0 .../{workspace_manager => }/watch_and_run.py | 6 +- .../{workspace_manager => }/watcher.py | 4 +- 91 files changed, 298 insertions(+), 140 deletions(-) create mode 100644 finecode_extension_api/.gitignore create mode 100644 finecode_extension_runner/.gitignore create mode 100644 finecode_extension_runner/README.md create mode 100644 finecode_extension_runner/pyproject.toml create mode 100644 finecode_extension_runner/src/finecode_extension_runner/README.md rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/__init__.py (100%) create mode 100644 finecode_extension_runner/src/finecode_extension_runner/__main__.py rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/_services/__init__.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/_services/run_action.py (97%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/action_handlers/__init__.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/action_handlers/dump_config.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/action_handlers/dump_config_save.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/action_handlers/prepare_envs_dump_configs.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/action_utils.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/api.proto (93%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/app_dirs.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/cli.py (90%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/context.py (93%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/di/__init__.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/di/_state.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/di/bootstrap.py (93%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/di/resolver.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/domain.py (97%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/global_state.py (85%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/impls/__init__.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/impls/action_runner.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/impls/command_runner.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/impls/file_manager.py (94%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/impls/inmemory_cache.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/impls/loguru_logger.py (96%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/impls/process_executor.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/impls/project_info_provider.py (100%) rename {src/finecode => finecode_extension_runner/src/finecode_extension_runner}/logs.py (94%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/lsp_server.py (97%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/partial_result_sender.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/project_dirs.py (91%) rename src/finecode/workspace_manager/__init__.py => finecode_extension_runner/src/finecode_extension_runner/py.typed (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/run_utils.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/schemas.py (100%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/services.py (95%) rename {src/finecode/extension_runner => finecode_extension_runner/src/finecode_extension_runner}/start.py (92%) rename {src/finecode/workspace_manager/cli_app => finecode_extension_runner/src/finecode_extension_runner/utils}/__init__.py (100%) rename src/finecode/{workspace_manager => }/app_dirs.py (100%) rename src/finecode/{workspace_manager => }/cli.py (95%) rename src/finecode/{workspace_manager/config => cli_app}/__init__.py (100%) rename src/finecode/{workspace_manager => }/cli_app/dump_config.py (91%) rename src/finecode/{workspace_manager => }/cli_app/prepare_envs.py (95%) rename src/finecode/{workspace_manager => }/cli_app/run.py (98%) rename src/finecode/{workspace_manager/lsp_server => config}/__init__.py (100%) rename src/finecode/{workspace_manager => }/config/collect_actions.py (91%) rename src/finecode/{workspace_manager => }/config/config_models.py (100%) rename src/finecode/{workspace_manager => }/config/dump_configs.py (100%) rename src/finecode/{workspace_manager => }/config/read_configs.py (94%) rename src/finecode/{workspace_manager => }/context.py (91%) rename src/finecode/{workspace_manager => }/domain.py (100%) delete mode 100644 src/finecode/extension_runner/README.md rename src/finecode/{workspace_manager => }/find_project.py (96%) rename src/finecode/{workspace_manager => }/finecode_cmd.py (100%) rename src/finecode/{workspace_manager => }/logger_utils.py (95%) rename src/finecode/{workspace_manager/lsp_server/endpoints => lsp_server}/__init__.py (100%) rename src/finecode/{workspace_manager => }/lsp_server/api.proto (97%) rename src/finecode/{workspace_manager/runner => lsp_server/endpoints}/__init__.py (100%) rename src/finecode/{workspace_manager => }/lsp_server/endpoints/action_tree.py (96%) rename src/finecode/{workspace_manager => }/lsp_server/endpoints/code_actions.py (96%) rename src/finecode/{workspace_manager => }/lsp_server/endpoints/code_lens.py (100%) rename src/finecode/{workspace_manager => }/lsp_server/endpoints/diagnostics.py (99%) rename src/finecode/{workspace_manager => }/lsp_server/endpoints/document_sync.py (95%) rename src/finecode/{workspace_manager => }/lsp_server/endpoints/formatting.py (96%) rename src/finecode/{workspace_manager => }/lsp_server/endpoints/inlay_hints.py (94%) rename src/finecode/{workspace_manager => }/lsp_server/global_state.py (82%) rename src/finecode/{workspace_manager => }/lsp_server/lsp_server.py (94%) rename src/finecode/{workspace_manager => }/lsp_server/schemas.py (100%) rename src/finecode/{workspace_manager => }/lsp_server/services.py (92%) rename src/finecode/{workspace_manager => }/main.py (92%) create mode 100644 src/finecode/mcp_server.py rename src/finecode/{workspace_manager => }/payload_preprocessor.py (96%) rename src/finecode/{workspace_manager => }/project_analyzer.py (100%) rename src/finecode/{workspace_manager => }/proxy_utils.py (96%) rename src/finecode/{workspace_manager/utils => runner}/__init__.py (100%) rename src/finecode/{workspace_manager => }/runner/manager.py (97%) rename src/finecode/{workspace_manager => }/runner/runner_client.py (98%) rename src/finecode/{workspace_manager => }/runner/runner_info.py (98%) rename src/finecode/{workspace_manager => }/services.py (96%) rename src/finecode/{workspace_manager => }/user_messages.py (100%) create mode 100644 src/finecode/utils/__init__.py rename src/finecode/{workspace_manager => }/utils/async_proc_queue.py (100%) rename src/finecode/{workspace_manager => }/utils/iterable_subscribe.py (100%) rename src/finecode/{workspace_manager => }/watch_and_run.py (91%) rename src/finecode/{workspace_manager => }/watcher.py (98%) diff --git a/.gitignore b/.gitignore index 0f6425a..6a63dc4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,9 @@ __pycache__ dist src/finecode/_version.py -src/finecode.egg-info \ No newline at end of file +src/finecode.egg-info + +.venvs +finecode_dump_config/* +!finecode_dump_config/pyproject.toml +build/ \ No newline at end of file diff --git a/finecode_extension_api/.gitignore b/finecode_extension_api/.gitignore new file mode 100644 index 0000000..ee1176b --- /dev/null +++ b/finecode_extension_api/.gitignore @@ -0,0 +1,5 @@ +.venvs/ +*.egg-info +build/ +finecode_dump_config/* +!finecode_dump_config/pyproject.toml \ No newline at end of file diff --git a/finecode_extension_runner/.gitignore b/finecode_extension_runner/.gitignore new file mode 100644 index 0000000..37ce05e --- /dev/null +++ b/finecode_extension_runner/.gitignore @@ -0,0 +1,14 @@ +__pycache__ +.coverage +.pytest_cache +.mypy_cache +.import_linter_cache +dist + +src/finecode_extension_runner/_version.py +src/finecode_extension_runner.egg-info + +.venvs +finecode_dump_config/* +!finecode_dump_config/pyproject.toml +build/ \ No newline at end of file diff --git a/finecode_extension_runner/README.md b/finecode_extension_runner/README.md new file mode 100644 index 0000000..0eefc7a --- /dev/null +++ b/finecode_extension_runner/README.md @@ -0,0 +1,5 @@ +# finecode_extension_runner + +Extension runner component for FineCode - handles execution environment for running actions in isolated environments. + +This package contains the extension runner that was previously part of the main finecode package (`finecode.extension_runner`). \ No newline at end of file diff --git a/finecode_extension_runner/pyproject.toml b/finecode_extension_runner/pyproject.toml new file mode 100644 index 0000000..d4d84eb --- /dev/null +++ b/finecode_extension_runner/pyproject.toml @@ -0,0 +1,61 @@ +[project] +name = "finecode_extension_runner" +dynamic = ["version"] +description = "Extension runner component for FineCode" +authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] +readme = "README.md" +requires-python = ">=3.11, < 3.14" +dependencies = [ + "loguru==0.7.*", + "tomlkit==0.11.*", + "click==8.1.*", + "pydantic==2.10.*", + "platformdirs==4.3.*", + "pygls==2.0.0-a2", + "finecode_extension_api @ file:///home/user/Development/FineCode/finecode/finecode_extension_api/dist/finecode_extension_api-0.1.1-py3-none-any.whl", + "ordered-set==4.1.*", + "fine_python_virtualenv @ file:///home/user/Development/FineCode/finecode/extensions/fine_python_virtualenv/dist/fine_python_virtualenv-0.1.0-py3-none-any.whl", + "fine_python_pip @ file:///home/user/Development/FineCode/finecode/extensions/fine_python_pip/dist/fine_python_pip-0.1.0-py3-none-any.whl", +] + +[dependency-groups] +dev_workspace = [ + "build==1.2.2.post1", + "finecode_extension_runner @ file:///home/user/Development/FineCode/finecode/finecode_extension_runner", +] +dev = [{ include-group = "runtime" }, "pytest==7.4.*", "debugpy==1.8.*"] +dev_no_runtime = [ + "fine_python_import_linter @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_import_linter", +] + +[build-system] +requires = [ + "setuptools>=64", + "setuptools-scm>=8", + "finecode_build_backend @ file:///home/user/Development/FineCode/finecode/finecode_build_backend", +] +build-backend = "finecode_build_backend.backend" + +[tool.poetry] +packages = [{ include = "finecode_extension_runner", from = "src" }] + +[tool.finecode] +presets = [{ source = "finecode_dev_common_preset" }] + +[tool.importlinter] +root_package = "finecode_extension_runner" +include_external_packages = true + +[[tool.importlinter.contracts]] +id = "er-layered" +name = "ER layered architecture" +type = "layers" +layers = [ + "finecode_extension_runner.lsp_server", + "finecode_extension_runner.services", + "finecode_extension_runner.domain", +] + +[tool.setuptools_scm] +version_file = "src/finecode_extension_runner/_version.py" +root = ".." diff --git a/finecode_extension_runner/src/finecode_extension_runner/README.md b/finecode_extension_runner/src/finecode_extension_runner/README.md new file mode 100644 index 0000000..b92e91e --- /dev/null +++ b/finecode_extension_runner/src/finecode_extension_runner/README.md @@ -0,0 +1 @@ +# FineCode Python Extension Runner diff --git a/src/finecode/extension_runner/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/__init__.py similarity index 100% rename from src/finecode/extension_runner/__init__.py rename to finecode_extension_runner/src/finecode_extension_runner/__init__.py diff --git a/finecode_extension_runner/src/finecode_extension_runner/__main__.py b/finecode_extension_runner/src/finecode_extension_runner/__main__.py new file mode 100644 index 0000000..1e32b11 --- /dev/null +++ b/finecode_extension_runner/src/finecode_extension_runner/__main__.py @@ -0,0 +1,4 @@ +from finecode_extension_runner import cli + +if __name__ == "__main__": + cli.cli() \ No newline at end of file diff --git a/src/finecode/extension_runner/_services/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/_services/__init__.py similarity index 100% rename from src/finecode/extension_runner/_services/__init__.py rename to finecode_extension_runner/src/finecode_extension_runner/_services/__init__.py diff --git a/src/finecode/extension_runner/_services/run_action.py b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py similarity index 97% rename from src/finecode/extension_runner/_services/run_action.py rename to finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py index d97f925..b1ae74f 100644 --- a/src/finecode/extension_runner/_services/run_action.py +++ b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py @@ -8,12 +8,12 @@ from loguru import logger from pydantic.dataclasses import dataclass as pydantic_dataclass -from finecode.extension_runner import project_dirs, run_utils, schemas, global_state, domain, context +from finecode_extension_runner import project_dirs, run_utils, schemas, global_state, domain, context from finecode_extension_api import code_action, textstyler -from finecode.extension_runner import ( +from finecode_extension_runner import ( partial_result_sender as partial_result_sender_module, ) -from finecode.extension_runner.di import resolver as di_resolver +from finecode_extension_runner.di import resolver as di_resolver last_run_id: int = 0 @@ -334,7 +334,6 @@ def resolve_func_args_with_di( return args - async def execute_action_handler( handler: domain.ActionHandler, payload: code_action.RunActionPayload | None, @@ -343,7 +342,7 @@ async def execute_action_handler( action_exec_info: domain.ActionExecInfo, action_context: code_action.ActionContext, runner_context: context.RunnerContext, -) -> code_action.RunActionResult | None: +) -> code_action.RunActionResult: logger.trace(f"R{run_id} | Run {handler.name} on {str(payload)[:100]}...") start_time = time.time_ns() execution_result: code_action.RunActionResult | None = None @@ -369,7 +368,9 @@ async def execute_action_handler( " could not be imported" ) logger.error(error) - return None + raise ActionFailedException( + f"Import of action handler '{handler.name}' failed(Run {run_id}): {handler.source}" + ) handler_raw_config = handler.config @@ -422,9 +423,12 @@ def get_process_executor(param_type): await initialize_callable_result except Exception as e: logger.error( - f"R{run_id} | Failed to initialize action {handler.name}: {e}" + f"R{run_id} | Failed to initialize action handler {handler.name}: {e}" + ) + raise ActionFailedException( + f"Initialisation of action handler '{handler.name}' failed(Run {run_id}): {e}" ) - return None + exec_info.status = domain.ActionHandlerExecInfoStatus.INITIALIZED def get_run_payload(param_type): diff --git a/src/finecode/extension_runner/action_handlers/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py similarity index 100% rename from src/finecode/extension_runner/action_handlers/__init__.py rename to finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py diff --git a/src/finecode/extension_runner/action_handlers/dump_config.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py similarity index 100% rename from src/finecode/extension_runner/action_handlers/dump_config.py rename to finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py diff --git a/src/finecode/extension_runner/action_handlers/dump_config_save.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py similarity index 100% rename from src/finecode/extension_runner/action_handlers/dump_config_save.py rename to finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py diff --git a/src/finecode/extension_runner/action_handlers/prepare_envs_dump_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py similarity index 100% rename from src/finecode/extension_runner/action_handlers/prepare_envs_dump_configs.py rename to finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py diff --git a/src/finecode/extension_runner/action_utils.py b/finecode_extension_runner/src/finecode_extension_runner/action_utils.py similarity index 100% rename from src/finecode/extension_runner/action_utils.py rename to finecode_extension_runner/src/finecode_extension_runner/action_utils.py diff --git a/src/finecode/extension_runner/api.proto b/finecode_extension_runner/src/finecode_extension_runner/api.proto similarity index 93% rename from src/finecode/extension_runner/api.proto rename to finecode_extension_runner/src/finecode_extension_runner/api.proto index 67c7eb6..2a13af6 100644 --- a/src/finecode/extension_runner/api.proto +++ b/finecode_extension_runner/src/finecode_extension_runner/api.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package finecode.extension_runner; +package finecode_extension_runner; message UpdateConfigRequest { string working_dir = 1; diff --git a/src/finecode/extension_runner/app_dirs.py b/finecode_extension_runner/src/finecode_extension_runner/app_dirs.py similarity index 100% rename from src/finecode/extension_runner/app_dirs.py rename to finecode_extension_runner/src/finecode_extension_runner/app_dirs.py diff --git a/src/finecode/extension_runner/cli.py b/finecode_extension_runner/src/finecode_extension_runner/cli.py similarity index 90% rename from src/finecode/extension_runner/cli.py rename to finecode_extension_runner/src/finecode_extension_runner/cli.py index c6d19b2..1cef1ef 100644 --- a/src/finecode/extension_runner/cli.py +++ b/finecode_extension_runner/src/finecode_extension_runner/cli.py @@ -5,8 +5,8 @@ import click from loguru import logger -import finecode.extension_runner.start as runner_start -from finecode.extension_runner import global_state +import finecode_extension_runner.start as runner_start +from finecode_extension_runner import global_state @click.group() @@ -50,7 +50,7 @@ def start(trace: bool, debug: bool, debug_port: int, project_path: Path, env_nam @main.command() def version(): """Show version information""" - package_version = metadata.version('finecode') + package_version = metadata.version('finecode_extension_runner') click.echo(f'FineCode Extension Runner {package_version}') diff --git a/src/finecode/extension_runner/context.py b/finecode_extension_runner/src/finecode_extension_runner/context.py similarity index 93% rename from src/finecode/extension_runner/context.py rename to finecode_extension_runner/src/finecode_extension_runner/context.py index a026ec0..6a54029 100644 --- a/src/finecode/extension_runner/context.py +++ b/finecode_extension_runner/src/finecode_extension_runner/context.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field -from finecode.extension_runner import domain +from finecode_extension_runner import domain from finecode_extension_api import code_action diff --git a/src/finecode/extension_runner/di/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/di/__init__.py similarity index 100% rename from src/finecode/extension_runner/di/__init__.py rename to finecode_extension_runner/src/finecode_extension_runner/di/__init__.py diff --git a/src/finecode/extension_runner/di/_state.py b/finecode_extension_runner/src/finecode_extension_runner/di/_state.py similarity index 100% rename from src/finecode/extension_runner/di/_state.py rename to finecode_extension_runner/src/finecode_extension_runner/di/_state.py diff --git a/src/finecode/extension_runner/di/bootstrap.py b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py similarity index 93% rename from src/finecode/extension_runner/di/bootstrap.py rename to finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py index 43a8ade..f6527ef 100644 --- a/src/finecode/extension_runner/di/bootstrap.py +++ b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py @@ -11,8 +11,8 @@ except ImportError: fine_python_mypy = None -from finecode.extension_runner import global_state -from finecode.extension_runner.impls import ( +from finecode_extension_runner import global_state +from finecode_extension_runner.impls import ( action_runner, command_runner, file_manager, @@ -30,8 +30,8 @@ iprojectinfoprovider ) from ._state import container, factories -from finecode.extension_runner import schemas -from finecode.extension_runner._services import run_action +from finecode_extension_runner import schemas +from finecode_extension_runner._services import run_action def bootstrap(get_document_func: Callable, save_document_func: Callable): diff --git a/src/finecode/extension_runner/di/resolver.py b/finecode_extension_runner/src/finecode_extension_runner/di/resolver.py similarity index 100% rename from src/finecode/extension_runner/di/resolver.py rename to finecode_extension_runner/src/finecode_extension_runner/di/resolver.py diff --git a/src/finecode/extension_runner/domain.py b/finecode_extension_runner/src/finecode_extension_runner/domain.py similarity index 97% rename from src/finecode/extension_runner/domain.py rename to finecode_extension_runner/src/finecode_extension_runner/domain.py index 46686a5..50a1f4c 100644 --- a/src/finecode/extension_runner/domain.py +++ b/finecode_extension_runner/src/finecode_extension_runner/domain.py @@ -4,7 +4,7 @@ import typing from pathlib import Path -from finecode.extension_runner.impls import process_executor as process_executor_impl +from finecode_extension_runner.impls import process_executor as process_executor_impl from finecode_extension_api import code_action diff --git a/src/finecode/extension_runner/global_state.py b/finecode_extension_runner/src/finecode_extension_runner/global_state.py similarity index 85% rename from src/finecode/extension_runner/global_state.py rename to finecode_extension_runner/src/finecode_extension_runner/global_state.py index 0e012e9..6282cc7 100644 --- a/src/finecode/extension_runner/global_state.py +++ b/finecode_extension_runner/src/finecode_extension_runner/global_state.py @@ -1,7 +1,7 @@ from pathlib import Path from typing import Literal -import finecode.extension_runner.context as context +import finecode_extension_runner.context as context runner_context: context.RunnerContext | None = None # it's the same as `runner_context.project.path`, but it's available from the start of diff --git a/src/finecode/extension_runner/impls/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/impls/__init__.py similarity index 100% rename from src/finecode/extension_runner/impls/__init__.py rename to finecode_extension_runner/src/finecode_extension_runner/impls/__init__.py diff --git a/src/finecode/extension_runner/impls/action_runner.py b/finecode_extension_runner/src/finecode_extension_runner/impls/action_runner.py similarity index 100% rename from src/finecode/extension_runner/impls/action_runner.py rename to finecode_extension_runner/src/finecode_extension_runner/impls/action_runner.py diff --git a/src/finecode/extension_runner/impls/command_runner.py b/finecode_extension_runner/src/finecode_extension_runner/impls/command_runner.py similarity index 100% rename from src/finecode/extension_runner/impls/command_runner.py rename to finecode_extension_runner/src/finecode_extension_runner/impls/command_runner.py diff --git a/src/finecode/extension_runner/impls/file_manager.py b/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py similarity index 94% rename from src/finecode/extension_runner/impls/file_manager.py rename to finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py index 0d7e2eb..d9d872e 100644 --- a/src/finecode/extension_runner/impls/file_manager.py +++ b/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py @@ -2,8 +2,7 @@ from pathlib import Path from typing import Callable -from finecode import pygls_types_utils -from finecode.extension_runner import domain +from finecode_extension_runner import domain from finecode_extension_api.interfaces import ifilemanager, ilogger @@ -37,7 +36,7 @@ async def get_content(self, file_path: Path) -> str: return file_content async def get_file_version(self, file_path: Path) -> str: - file_uri = pygls_types_utils.path_to_uri_str(file_path) + file_uri = path_to_uri_str(file_path) file_version: str = "" if file_uri in self.docs_owned_by_client: @@ -68,7 +67,7 @@ async def get_file_version(self, file_path: Path) -> str: return file_version async def save_file(self, file_path: Path, file_content: str) -> None: - file_uri = pygls_types_utils.path_to_uri_str(file_path) + file_uri = path_to_uri_str(file_path) if file_uri in self.docs_owned_by_client: await self.save_document_func(file_uri, file_content) else: @@ -96,3 +95,7 @@ def get_hash_of_file_from_fs(self, file_path: Path) -> str: file_version = hashlib.file_digest(f, "sha256").hexdigest() return file_version + + +def path_to_uri_str(path: Path) -> str: + return f"file://{path.as_posix()}" diff --git a/src/finecode/extension_runner/impls/inmemory_cache.py b/finecode_extension_runner/src/finecode_extension_runner/impls/inmemory_cache.py similarity index 100% rename from src/finecode/extension_runner/impls/inmemory_cache.py rename to finecode_extension_runner/src/finecode_extension_runner/impls/inmemory_cache.py diff --git a/src/finecode/extension_runner/impls/loguru_logger.py b/finecode_extension_runner/src/finecode_extension_runner/impls/loguru_logger.py similarity index 96% rename from src/finecode/extension_runner/impls/loguru_logger.py rename to finecode_extension_runner/src/finecode_extension_runner/impls/loguru_logger.py index db07c77..c5536f3 100644 --- a/src/finecode/extension_runner/impls/loguru_logger.py +++ b/finecode_extension_runner/src/finecode_extension_runner/impls/loguru_logger.py @@ -7,7 +7,7 @@ # else: # from typing import override -# from finecode.extension_runner.interfaces import ilogger +# from finecode_extension_runner.interfaces import ilogger # class LoguruLogger(ilogger.ILogger): diff --git a/src/finecode/extension_runner/impls/process_executor.py b/finecode_extension_runner/src/finecode_extension_runner/impls/process_executor.py similarity index 100% rename from src/finecode/extension_runner/impls/process_executor.py rename to finecode_extension_runner/src/finecode_extension_runner/impls/process_executor.py diff --git a/src/finecode/extension_runner/impls/project_info_provider.py b/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py similarity index 100% rename from src/finecode/extension_runner/impls/project_info_provider.py rename to finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py diff --git a/src/finecode/logs.py b/finecode_extension_runner/src/finecode_extension_runner/logs.py similarity index 94% rename from src/finecode/logs.py rename to finecode_extension_runner/src/finecode_extension_runner/logs.py index 22dbdd6..0180c70 100644 --- a/src/finecode/logs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/logs.py @@ -68,3 +68,10 @@ def set_log_level_for_group(group: str, level: LogLevel | None): def reset_log_level_for_group(group: str): if group in log_level_by_group: del log_level_by_group[group] + + +__all__ = [ + 'save_logs_to_file', + 'set_log_level_for_group', + 'reset_log_level_for_group' +] \ No newline at end of file diff --git a/src/finecode/extension_runner/lsp_server.py b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py similarity index 97% rename from src/finecode/extension_runner/lsp_server.py rename to finecode_extension_runner/src/finecode_extension_runner/lsp_server.py index baf52f1..4c95a7b 100644 --- a/src/finecode/extension_runner/lsp_server.py +++ b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py @@ -15,9 +15,9 @@ from lsprotocol import types from pygls.lsp import server as lsp_server -from finecode.extension_runner import domain, schemas, services -from finecode.extension_runner._services import run_action as run_action_service -from finecode.extension_runner.impls import project_info_provider +from finecode_extension_runner import domain, schemas, services +from finecode_extension_runner._services import run_action as run_action_service +from finecode_extension_runner.impls import project_info_provider from finecode_extension_api import code_action diff --git a/src/finecode/extension_runner/partial_result_sender.py b/finecode_extension_runner/src/finecode_extension_runner/partial_result_sender.py similarity index 100% rename from src/finecode/extension_runner/partial_result_sender.py rename to finecode_extension_runner/src/finecode_extension_runner/partial_result_sender.py diff --git a/src/finecode/extension_runner/project_dirs.py b/finecode_extension_runner/src/finecode_extension_runner/project_dirs.py similarity index 91% rename from src/finecode/extension_runner/project_dirs.py rename to finecode_extension_runner/src/finecode_extension_runner/project_dirs.py index b21bbd5..347cb40 100644 --- a/src/finecode/extension_runner/project_dirs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/project_dirs.py @@ -2,7 +2,7 @@ import os from pathlib import Path -from finecode.extension_runner import app_dirs +from finecode_extension_runner import app_dirs def get_project_dir(project_path: Path) -> Path: diff --git a/src/finecode/workspace_manager/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/py.typed similarity index 100% rename from src/finecode/workspace_manager/__init__.py rename to finecode_extension_runner/src/finecode_extension_runner/py.typed diff --git a/src/finecode/extension_runner/run_utils.py b/finecode_extension_runner/src/finecode_extension_runner/run_utils.py similarity index 100% rename from src/finecode/extension_runner/run_utils.py rename to finecode_extension_runner/src/finecode_extension_runner/run_utils.py diff --git a/src/finecode/extension_runner/schemas.py b/finecode_extension_runner/src/finecode_extension_runner/schemas.py similarity index 100% rename from src/finecode/extension_runner/schemas.py rename to finecode_extension_runner/src/finecode_extension_runner/schemas.py diff --git a/src/finecode/extension_runner/services.py b/finecode_extension_runner/src/finecode_extension_runner/services.py similarity index 95% rename from src/finecode/extension_runner/services.py rename to finecode_extension_runner/src/finecode_extension_runner/services.py index 7faa3c7..1e9fe5d 100644 --- a/src/finecode/extension_runner/services.py +++ b/finecode_extension_runner/src/finecode_extension_runner/services.py @@ -11,12 +11,12 @@ from loguru import logger from pydantic.dataclasses import dataclass as pydantic_dataclass -from finecode.extension_runner import context, domain, global_state -from finecode.extension_runner import project_dirs, run_utils, schemas +from finecode_extension_runner import context, domain, global_state +from finecode_extension_runner import project_dirs, run_utils, schemas from finecode_extension_api import code_action, textstyler -from finecode.extension_runner._services import run_action as run_action_module -from finecode.extension_runner._services.run_action import run_action -from finecode.extension_runner.di import bootstrap as di_bootstrap +from finecode_extension_runner._services import run_action as run_action_module +from finecode_extension_runner._services.run_action import run_action +from finecode_extension_runner.di import bootstrap as di_bootstrap document_requester: typing.Callable diff --git a/src/finecode/extension_runner/start.py b/finecode_extension_runner/src/finecode_extension_runner/start.py similarity index 92% rename from src/finecode/extension_runner/start.py rename to finecode_extension_runner/src/finecode_extension_runner/start.py index 5be79ef..8bf7ee3 100644 --- a/src/finecode/extension_runner/start.py +++ b/finecode_extension_runner/src/finecode_extension_runner/start.py @@ -4,9 +4,9 @@ from loguru import logger -import finecode.extension_runner.global_state as global_state -import finecode.extension_runner.lsp_server as extension_runner_lsp -from finecode import logs +import finecode_extension_runner.global_state as global_state +import finecode_extension_runner.lsp_server as extension_runner_lsp +from finecode_extension_runner import logs # import finecode.pygls_server_utils as pygls_server_utils @@ -93,10 +93,10 @@ def emit(self, record: logging.LogRecord) -> None: # TODO: make configurable logs.set_log_level_for_group( - "finecode.extension_runner.impls.file_manager", logs.LogLevel.WARNING + "finecode_extension_runner.impls.file_manager", logs.LogLevel.WARNING ) logs.set_log_level_for_group( - "finecode.extension_runner.impls.inmemory_cache", logs.LogLevel.WARNING + "finecode_extension_runner.impls.inmemory_cache", logs.LogLevel.WARNING ) logger.info(f"Python executable: {sys.executable}") diff --git a/src/finecode/workspace_manager/cli_app/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/utils/__init__.py similarity index 100% rename from src/finecode/workspace_manager/cli_app/__init__.py rename to finecode_extension_runner/src/finecode_extension_runner/utils/__init__.py diff --git a/src/finecode/__main__.py b/src/finecode/__main__.py index 758e4b0..487132e 100644 --- a/src/finecode/__main__.py +++ b/src/finecode/__main__.py @@ -1,4 +1,4 @@ -from finecode.workspace_manager import cli as wm_cli +from finecode import cli as wm_cli if __name__ == "__main__": wm_cli.cli() diff --git a/src/finecode/workspace_manager/app_dirs.py b/src/finecode/app_dirs.py similarity index 100% rename from src/finecode/workspace_manager/app_dirs.py rename to src/finecode/app_dirs.py diff --git a/src/finecode/workspace_manager/cli.py b/src/finecode/cli.py similarity index 95% rename from src/finecode/workspace_manager/cli.py rename to src/finecode/cli.py index ca3c34d..dbdbc50 100644 --- a/src/finecode/workspace_manager/cli.py +++ b/src/finecode/cli.py @@ -6,12 +6,12 @@ import click from loguru import logger -import finecode.workspace_manager.main as workspace_manager +import finecode.main as workspace_manager from finecode import communication_utils -from finecode.workspace_manager import logger_utils, user_messages -from finecode.workspace_manager.cli_app import run as run_cmd -from finecode.workspace_manager.cli_app import dump_config as dump_config_cmd -from finecode.workspace_manager.cli_app import prepare_envs as prepare_envs_cmd +from finecode import logger_utils, user_messages +from finecode.cli_app import run as run_cmd +from finecode.cli_app import dump_config as dump_config_cmd +from finecode.cli_app import prepare_envs as prepare_envs_cmd @click.group() diff --git a/src/finecode/workspace_manager/config/__init__.py b/src/finecode/cli_app/__init__.py similarity index 100% rename from src/finecode/workspace_manager/config/__init__.py rename to src/finecode/cli_app/__init__.py diff --git a/src/finecode/workspace_manager/cli_app/dump_config.py b/src/finecode/cli_app/dump_config.py similarity index 91% rename from src/finecode/workspace_manager/cli_app/dump_config.py rename to src/finecode/cli_app/dump_config.py index 5abf41e..57bfcfd 100644 --- a/src/finecode/workspace_manager/cli_app/dump_config.py +++ b/src/finecode/cli_app/dump_config.py @@ -3,9 +3,9 @@ from loguru import logger -from finecode.workspace_manager import context, services -from finecode.workspace_manager.config import read_configs, dump_configs -from finecode.workspace_manager.runner import manager as runner_manager +from finecode import context, services +from finecode.config import read_configs, dump_configs +from finecode.runner import manager as runner_manager class DumpFailed(Exception): diff --git a/src/finecode/workspace_manager/cli_app/prepare_envs.py b/src/finecode/cli_app/prepare_envs.py similarity index 95% rename from src/finecode/workspace_manager/cli_app/prepare_envs.py rename to src/finecode/cli_app/prepare_envs.py index 7d4ec8f..4c665ea 100644 --- a/src/finecode/workspace_manager/cli_app/prepare_envs.py +++ b/src/finecode/cli_app/prepare_envs.py @@ -2,10 +2,10 @@ import shutil from loguru import logger -from finecode.workspace_manager import context, services, domain -from finecode.workspace_manager.config import read_configs, collect_actions -from finecode.workspace_manager.cli_app import run as run_cli -from finecode.workspace_manager.runner import manager as runner_manager +from finecode import context, services, domain +from finecode.config import read_configs, collect_actions +from finecode.cli_app import run as run_cli +from finecode.runner import manager as runner_manager async def prepare_envs(workdir_path: pathlib.Path) -> None: diff --git a/src/finecode/workspace_manager/cli_app/run.py b/src/finecode/cli_app/run.py similarity index 98% rename from src/finecode/workspace_manager/cli_app/run.py rename to src/finecode/cli_app/run.py index 28cbde8..a72c257 100644 --- a/src/finecode/workspace_manager/cli_app/run.py +++ b/src/finecode/cli_app/run.py @@ -6,9 +6,9 @@ import ordered_set from loguru import logger -from finecode.workspace_manager import context, domain, services -from finecode.workspace_manager.config import read_configs, collect_actions -from finecode.workspace_manager.runner import manager as runner_manager +from finecode import context, domain, services +from finecode.config import read_configs, collect_actions +from finecode.runner import manager as runner_manager class RunFailed(Exception): diff --git a/src/finecode/workspace_manager/lsp_server/__init__.py b/src/finecode/config/__init__.py similarity index 100% rename from src/finecode/workspace_manager/lsp_server/__init__.py rename to src/finecode/config/__init__.py diff --git a/src/finecode/workspace_manager/config/collect_actions.py b/src/finecode/config/collect_actions.py similarity index 91% rename from src/finecode/workspace_manager/config/collect_actions.py rename to src/finecode/config/collect_actions.py index 9f2435e..6c2196a 100644 --- a/src/finecode/workspace_manager/config/collect_actions.py +++ b/src/finecode/config/collect_actions.py @@ -1,9 +1,9 @@ from pathlib import Path from typing import Any -import finecode.workspace_manager.config.config_models as config_models -import finecode.workspace_manager.context as context -import finecode.workspace_manager.domain as domain +import finecode.config.config_models as config_models +import finecode.context as context +import finecode.domain as domain def collect_actions( diff --git a/src/finecode/workspace_manager/config/config_models.py b/src/finecode/config/config_models.py similarity index 100% rename from src/finecode/workspace_manager/config/config_models.py rename to src/finecode/config/config_models.py diff --git a/src/finecode/workspace_manager/config/dump_configs.py b/src/finecode/config/dump_configs.py similarity index 100% rename from src/finecode/workspace_manager/config/dump_configs.py rename to src/finecode/config/dump_configs.py diff --git a/src/finecode/workspace_manager/config/read_configs.py b/src/finecode/config/read_configs.py similarity index 94% rename from src/finecode/workspace_manager/config/read_configs.py rename to src/finecode/config/read_configs.py index eced811..d9c7aad 100644 --- a/src/finecode/workspace_manager/config/read_configs.py +++ b/src/finecode/config/read_configs.py @@ -1,12 +1,13 @@ from pathlib import Path from typing import Any, NamedTuple +from importlib import metadata from loguru import logger from tomlkit import loads as toml_loads -from finecode.workspace_manager import context, domain, user_messages -from finecode.workspace_manager.config import config_models -from finecode.workspace_manager.runner import runner_client, runner_info +from finecode import context, domain, user_messages +from finecode.config import config_models +from finecode.runner import runner_client, runner_info async def read_projects_in_dir( @@ -84,7 +85,7 @@ async def read_project_config( source='finecode_extension_api.actions.prepare_envs.PrepareEnvsAction', handlers=[ domain.ActionHandler(name='prepare_envs_venvs', source='fine_python_virtualenv.VirtualenvPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_virtualenv==0.1.*']), - domain.ActionHandler(name='prepare_envs_dump_configs', source='finecode.extension_runner.action_handlers.PrepareEnvsDumpConfigsHandler', config={}, env='dev_workspace', dependencies=[]), + domain.ActionHandler(name='prepare_envs_dump_configs', source='finecode_extension_runner.action_handlers.PrepareEnvsDumpConfigsHandler', config={}, env='dev_workspace', dependencies=[]), domain.ActionHandler(name='prepare_envs_pip', source='fine_python_pip.PipPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_pip==0.1.*']) ], config={} @@ -112,8 +113,8 @@ async def read_project_config( name='dump_config', source='finecode_extension_api.actions.dump_config.DumpConfigAction', handlers=[ - domain.ActionHandler(name='dump_config', source='finecode.extension_runner.action_handlers.DumpConfigHandler', config={}, env='dev_workspace', dependencies=[]), - domain.ActionHandler(name='dump_config_save', source='finecode.extension_runner.action_handlers.DumpConfigSaveHandler', config={}, env='dev_workspace', dependencies=[]) + domain.ActionHandler(name='dump_config', source='finecode_extension_runner.action_handlers.DumpConfigHandler', config={}, env='dev_workspace', dependencies=[]), + domain.ActionHandler(name='dump_config_save', source='finecode_extension_runner.action_handlers.DumpConfigSaveHandler', config={}, env='dev_workspace', dependencies=[]) ], config={} ) @@ -122,6 +123,8 @@ async def read_project_config( # add runtime dependency group if it's not explicitly declared add_runtime_dependency_group_if_new(project_def) + add_extension_runner_to_dependencies(project_def) + merge_handlers_dependencies_into_groups(project_def) ws_context.ws_projects_raw_configs[project.dir_path] = project_def @@ -428,3 +431,19 @@ def merge_handlers_dependencies_into_groups(project_config: dict[str, Any]) -> N unique_deps.append(dep) deps_groups[group_name] = unique_deps + + +def add_extension_runner_to_dependencies(project_config: dict[str, Any]) -> None: + try: + deps_groups = project_config['dependency-groups'] + except KeyError: + return + + finecode_version = metadata.version('finecode') + + for group_name, group_packages in deps_groups.items(): + if group_name == 'dev_workspace': + # skip `dev_workspace` because it contains finecode already + continue + + group_packages.append(f'finecode_extension_runner == {finecode_version}') diff --git a/src/finecode/workspace_manager/context.py b/src/finecode/context.py similarity index 91% rename from src/finecode/workspace_manager/context.py rename to src/finecode/context.py index f790b61..ffbcca1 100644 --- a/src/finecode/workspace_manager/context.py +++ b/src/finecode/context.py @@ -4,10 +4,10 @@ from pathlib import Path from typing import TYPE_CHECKING, Any -from finecode.workspace_manager import domain +from finecode import domain if TYPE_CHECKING: - from finecode.workspace_manager.runner.runner_info import ExtensionRunnerInfo + from finecode.runner.runner_info import ExtensionRunnerInfo @dataclass diff --git a/src/finecode/workspace_manager/domain.py b/src/finecode/domain.py similarity index 100% rename from src/finecode/workspace_manager/domain.py rename to src/finecode/domain.py diff --git a/src/finecode/extension_runner/README.md b/src/finecode/extension_runner/README.md deleted file mode 100644 index 97514be..0000000 --- a/src/finecode/extension_runner/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# FineCode Python Extension Runner - -## Development Notes - -- extension server could use tcp communication with workspace manager, but for some reason it didn't work in first tries: it blocked the server, client connection was accepted, but no requests were processed. - -## Data Provider - -dataKind: PythonPackageList - -provider: PythonPackageListProvider - -all data must be versioned - -providers calculate data either automatically or on demand diff --git a/src/finecode/workspace_manager/find_project.py b/src/finecode/find_project.py similarity index 96% rename from src/finecode/workspace_manager/find_project.py rename to src/finecode/find_project.py index bfe0f92..3be854a 100644 --- a/src/finecode/workspace_manager/find_project.py +++ b/src/finecode/find_project.py @@ -2,8 +2,8 @@ from loguru import logger -from finecode.workspace_manager import domain -from finecode.workspace_manager.context import WorkspaceContext +from finecode import domain +from finecode.context import WorkspaceContext class FileNotInWorkspaceException(BaseException): ... diff --git a/src/finecode/workspace_manager/finecode_cmd.py b/src/finecode/finecode_cmd.py similarity index 100% rename from src/finecode/workspace_manager/finecode_cmd.py rename to src/finecode/finecode_cmd.py diff --git a/src/finecode/workspace_manager/logger_utils.py b/src/finecode/logger_utils.py similarity index 95% rename from src/finecode/workspace_manager/logger_utils.py rename to src/finecode/logger_utils.py index b3a7212..090c5ba 100644 --- a/src/finecode/workspace_manager/logger_utils.py +++ b/src/finecode/logger_utils.py @@ -4,8 +4,8 @@ from loguru import logger -from finecode import logs -from finecode.workspace_manager import app_dirs +from finecode_extension_runner import logs +from finecode import app_dirs def init_logger(trace: bool, stdout: bool = False): diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/__init__.py b/src/finecode/lsp_server/__init__.py similarity index 100% rename from src/finecode/workspace_manager/lsp_server/endpoints/__init__.py rename to src/finecode/lsp_server/__init__.py diff --git a/src/finecode/workspace_manager/lsp_server/api.proto b/src/finecode/lsp_server/api.proto similarity index 97% rename from src/finecode/workspace_manager/lsp_server/api.proto rename to src/finecode/lsp_server/api.proto index 60fa907..0f594e9 100644 --- a/src/finecode/workspace_manager/lsp_server/api.proto +++ b/src/finecode/lsp_server/api.proto @@ -1,6 +1,6 @@ syntax = "proto3"; -package finecode.workspace_manager; +package finecode; message AddWorkspaceDirRequest { string dir_path = 1; diff --git a/src/finecode/workspace_manager/runner/__init__.py b/src/finecode/lsp_server/endpoints/__init__.py similarity index 100% rename from src/finecode/workspace_manager/runner/__init__.py rename to src/finecode/lsp_server/endpoints/__init__.py diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py b/src/finecode/lsp_server/endpoints/action_tree.py similarity index 96% rename from src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py rename to src/finecode/lsp_server/endpoints/action_tree.py index b3b3784..70e477b 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/action_tree.py +++ b/src/finecode/lsp_server/endpoints/action_tree.py @@ -5,12 +5,12 @@ import ordered_set from pygls.lsp.server import LanguageServer -from finecode.workspace_manager import context, domain -from finecode.workspace_manager import services as wm_services -from finecode.workspace_manager import user_messages -from finecode.workspace_manager.lsp_server import global_state, schemas -from finecode.workspace_manager.lsp_server.services import ActionNotFound, InternalError -from finecode.workspace_manager.runner import runner_client +from finecode import context, domain +from finecode import services as wm_services +from finecode import user_messages +from finecode.lsp_server import global_state, schemas +from finecode.lsp_server.services import ActionNotFound, InternalError +from finecode.runner import runner_client async def list_actions(ls: LanguageServer, params): diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/code_actions.py b/src/finecode/lsp_server/endpoints/code_actions.py similarity index 96% rename from src/finecode/workspace_manager/lsp_server/endpoints/code_actions.py rename to src/finecode/lsp_server/endpoints/code_actions.py index 44845b3..afbd007 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/code_actions.py +++ b/src/finecode/lsp_server/endpoints/code_actions.py @@ -7,7 +7,7 @@ from lsprotocol import types # from finecode import pygls_types_utils, lsp_types -# from finecode.workspace_manager.server import global_state, proxy_utils +# from finecode.server import global_state, proxy_utils if TYPE_CHECKING: from pygls.lsp.server import LanguageServer diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/code_lens.py b/src/finecode/lsp_server/endpoints/code_lens.py similarity index 100% rename from src/finecode/workspace_manager/lsp_server/endpoints/code_lens.py rename to src/finecode/lsp_server/endpoints/code_lens.py diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py b/src/finecode/lsp_server/endpoints/diagnostics.py similarity index 99% rename from src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py rename to src/finecode/lsp_server/endpoints/diagnostics.py index 3dad26b..4239c45 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/diagnostics.py +++ b/src/finecode/lsp_server/endpoints/diagnostics.py @@ -10,8 +10,8 @@ from lsprotocol import types from finecode import pygls_types_utils -from finecode.workspace_manager import domain, project_analyzer, proxy_utils, services, context -from finecode.workspace_manager.lsp_server import global_state +from finecode import domain, project_analyzer, proxy_utils, services, context +from finecode.lsp_server import global_state from finecode_extension_api.actions import lint as lint_action if TYPE_CHECKING: diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/document_sync.py b/src/finecode/lsp_server/endpoints/document_sync.py similarity index 95% rename from src/finecode/workspace_manager/lsp_server/endpoints/document_sync.py rename to src/finecode/lsp_server/endpoints/document_sync.py index 5116830..b05a279 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/document_sync.py +++ b/src/finecode/lsp_server/endpoints/document_sync.py @@ -5,9 +5,9 @@ from lsprotocol import types from pygls.lsp.server import LanguageServer -from finecode.workspace_manager import domain -from finecode.workspace_manager.lsp_server import global_state -from finecode.workspace_manager.runner import runner_client, runner_info +from finecode import domain +from finecode.lsp_server import global_state +from finecode.runner import runner_client, runner_info async def document_did_open( diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/formatting.py b/src/finecode/lsp_server/endpoints/formatting.py similarity index 96% rename from src/finecode/workspace_manager/lsp_server/endpoints/formatting.py rename to src/finecode/lsp_server/endpoints/formatting.py index ce32c15..c2c207e 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/formatting.py +++ b/src/finecode/lsp_server/endpoints/formatting.py @@ -6,8 +6,8 @@ from lsprotocol import types from finecode import pygls_types_utils -from finecode.workspace_manager import proxy_utils -from finecode.workspace_manager.lsp_server import global_state +from finecode import proxy_utils +from finecode.lsp_server import global_state if TYPE_CHECKING: from pygls.lsp.server import LanguageServer diff --git a/src/finecode/workspace_manager/lsp_server/endpoints/inlay_hints.py b/src/finecode/lsp_server/endpoints/inlay_hints.py similarity index 94% rename from src/finecode/workspace_manager/lsp_server/endpoints/inlay_hints.py rename to src/finecode/lsp_server/endpoints/inlay_hints.py index f994520..50e3382 100644 --- a/src/finecode/workspace_manager/lsp_server/endpoints/inlay_hints.py +++ b/src/finecode/lsp_server/endpoints/inlay_hints.py @@ -6,8 +6,8 @@ from lsprotocol import types from finecode import pygls_types_utils -from finecode.workspace_manager import find_project, proxy_utils -from finecode.workspace_manager.lsp_server import global_state +from finecode import find_project, proxy_utils +from finecode.lsp_server import global_state if TYPE_CHECKING: from pygls.lsp.server import LanguageServer diff --git a/src/finecode/workspace_manager/lsp_server/global_state.py b/src/finecode/lsp_server/global_state.py similarity index 82% rename from src/finecode/workspace_manager/lsp_server/global_state.py rename to src/finecode/lsp_server/global_state.py index 4740e17..61df4ce 100644 --- a/src/finecode/workspace_manager/lsp_server/global_state.py +++ b/src/finecode/lsp_server/global_state.py @@ -2,7 +2,7 @@ import collections.abc from typing import Any -from finecode.workspace_manager import context +from finecode import context ws_context = context.WorkspaceContext([]) server_initialized = asyncio.Event() diff --git a/src/finecode/workspace_manager/lsp_server/lsp_server.py b/src/finecode/lsp_server/lsp_server.py similarity index 94% rename from src/finecode/workspace_manager/lsp_server/lsp_server.py rename to src/finecode/lsp_server/lsp_server.py index ce25ea1..0408070 100644 --- a/src/finecode/workspace_manager/lsp_server/lsp_server.py +++ b/src/finecode/lsp_server/lsp_server.py @@ -7,27 +7,27 @@ from lsprotocol import types from pygls.lsp.server import LanguageServer -from finecode.workspace_manager import services as wm_services -from finecode.workspace_manager.lsp_server import global_state, schemas, services -from finecode.workspace_manager.lsp_server.endpoints import ( +from finecode import services as wm_services +from finecode.lsp_server import global_state, schemas, services +from finecode.lsp_server.endpoints import ( action_tree as action_tree_endpoints, ) -from finecode.workspace_manager.lsp_server.endpoints import ( +from finecode.lsp_server.endpoints import ( code_actions as code_actions_endpoints, ) -from finecode.workspace_manager.lsp_server.endpoints import ( +from finecode.lsp_server.endpoints import ( code_lens as code_lens_endpoints, ) -from finecode.workspace_manager.lsp_server.endpoints import ( +from finecode.lsp_server.endpoints import ( diagnostics as diagnostics_endpoints, ) -from finecode.workspace_manager.lsp_server.endpoints import ( +from finecode.lsp_server.endpoints import ( document_sync as document_sync_endpoints, ) -from finecode.workspace_manager.lsp_server.endpoints import ( +from finecode.lsp_server.endpoints import ( formatting as formatting_endpoints, ) -from finecode.workspace_manager.lsp_server.endpoints import ( +from finecode.lsp_server.endpoints import ( inlay_hints as inlay_hints_endpoints, ) diff --git a/src/finecode/workspace_manager/lsp_server/schemas.py b/src/finecode/lsp_server/schemas.py similarity index 100% rename from src/finecode/workspace_manager/lsp_server/schemas.py rename to src/finecode/lsp_server/schemas.py diff --git a/src/finecode/workspace_manager/lsp_server/services.py b/src/finecode/lsp_server/services.py similarity index 92% rename from src/finecode/workspace_manager/lsp_server/services.py rename to src/finecode/lsp_server/services.py index e1ec5ed..26a7c85 100644 --- a/src/finecode/workspace_manager/lsp_server/services.py +++ b/src/finecode/lsp_server/services.py @@ -2,10 +2,10 @@ from loguru import logger -from finecode.workspace_manager import domain, user_messages -from finecode.workspace_manager.config import read_configs -from finecode.workspace_manager.lsp_server import global_state, schemas -from finecode.workspace_manager.runner import manager as runner_manager +from finecode import domain, user_messages +from finecode.config import read_configs +from finecode.lsp_server import global_state, schemas +from finecode.runner import manager as runner_manager class ActionNotFound(Exception): ... diff --git a/src/finecode/workspace_manager/main.py b/src/finecode/main.py similarity index 92% rename from src/finecode/workspace_manager/main.py rename to src/finecode/main.py index 827453c..6752726 100644 --- a/src/finecode/workspace_manager/main.py +++ b/src/finecode/main.py @@ -1,8 +1,8 @@ from __future__ import annotations from finecode import communication_utils # pygls_server_utils -from finecode.workspace_manager import logger_utils -from finecode.workspace_manager.lsp_server.lsp_server import create_lsp_server +from finecode import logger_utils +from finecode.lsp_server.lsp_server import create_lsp_server # async def start( # comm_type: communication_utils.CommunicationType, diff --git a/src/finecode/mcp_server.py b/src/finecode/mcp_server.py new file mode 100644 index 0000000..ee0b132 --- /dev/null +++ b/src/finecode/mcp_server.py @@ -0,0 +1,43 @@ +import asyncio +import pathlib +from mcp.server.fastmcp import FastMCP + +from finecode import context + + +async def create_and_start_mcp_server(port: int, ws_context: context.WorkspaceContext) -> asyncio.Task: + # TODO: use Server instead of FastMCP to be able to call tools dynamically + # example: https://github.com/modelcontextprotocol/servers/blob/main/src/git/src/mcp_server_git/server.py + mcp = FastMCP("FineCode", port=8776, json_response=True) + + def list_actions(): + return { + "actions": [ + { "name": "Lint" }, + { "name": "Format" } + ] + } + + mcp.add_tool(fn=list_actions, name="list_actions", description="List actions available for developer in development workspace", annotations=None) + + def lint(): + print("perform linting...") + + mcp.add_tool(fn=lint, name="lint", description="Lint either the whole workspace or single project or even file", annotations=None) + # TODO: ~~projects as resource?~~ + # TODO: finecode actions as mcp tools? + + mcp.call_tool + # mcp.run() + server_task = asyncio.create_task(mcp.run_streamable_http_async()) + return server_task + + +async def start(): + ws_context = context.WorkspaceContext([pathlib.Path('/home/user/Development/FineCode/finecode')]) + await create_and_start_mcp_server(8776, ws_context) + while True: + await asyncio.sleep(1) + +if __name__ == "__main__": + asyncio.run(start()) diff --git a/src/finecode/workspace_manager/payload_preprocessor.py b/src/finecode/payload_preprocessor.py similarity index 96% rename from src/finecode/workspace_manager/payload_preprocessor.py rename to src/finecode/payload_preprocessor.py index dc2dd77..28899e8 100644 --- a/src/finecode/workspace_manager/payload_preprocessor.py +++ b/src/finecode/payload_preprocessor.py @@ -1,7 +1,7 @@ import pathlib import typing -from finecode.workspace_manager import context, project_analyzer +from finecode import context, project_analyzer def preprocess_for_project( diff --git a/src/finecode/workspace_manager/project_analyzer.py b/src/finecode/project_analyzer.py similarity index 100% rename from src/finecode/workspace_manager/project_analyzer.py rename to src/finecode/project_analyzer.py diff --git a/src/finecode/workspace_manager/proxy_utils.py b/src/finecode/proxy_utils.py similarity index 96% rename from src/finecode/workspace_manager/proxy_utils.py rename to src/finecode/proxy_utils.py index 6f7e2c6..65b902a 100644 --- a/src/finecode/workspace_manager/proxy_utils.py +++ b/src/finecode/proxy_utils.py @@ -7,10 +7,10 @@ from loguru import logger import ordered_set -from finecode.workspace_manager import context, domain, find_project, services -from finecode.workspace_manager.services import ActionRunFailed -from finecode.workspace_manager.runner import manager as runner_manager -from finecode.workspace_manager.runner import runner_client, runner_info +from finecode import context, domain, find_project, services +from finecode.services import ActionRunFailed +from finecode.runner import manager as runner_manager +from finecode.runner import runner_client, runner_info def find_action_project( diff --git a/src/finecode/workspace_manager/utils/__init__.py b/src/finecode/runner/__init__.py similarity index 100% rename from src/finecode/workspace_manager/utils/__init__.py rename to src/finecode/runner/__init__.py diff --git a/src/finecode/workspace_manager/runner/manager.py b/src/finecode/runner/manager.py similarity index 97% rename from src/finecode/workspace_manager/runner/manager.py rename to src/finecode/runner/manager.py index 767d9b9..202e0ed 100644 --- a/src/finecode/workspace_manager/runner/manager.py +++ b/src/finecode/runner/manager.py @@ -9,10 +9,10 @@ from finecode import dirs_utils from finecode.pygls_client_utils import create_lsp_client_io -from finecode.workspace_manager import context, domain, finecode_cmd -from finecode.workspace_manager.config import collect_actions, read_configs -from finecode.workspace_manager.runner import runner_client, runner_info -from finecode.workspace_manager.utils import iterable_subscribe +from finecode import context, domain, finecode_cmd +from finecode.config import collect_actions, read_configs +from finecode.runner import runner_client, runner_info +from finecode.utils import iterable_subscribe project_changed_callback: ( Callable[[domain.Project], Coroutine[None, None, None]] | None @@ -94,7 +94,7 @@ async def start_extension_runner( process_args_str: str = " ".join(process_args) client = await create_lsp_client_io( runner_info.CustomJsonRpcClient, - f"{python_cmd} -m finecode.extension_runner.cli start {process_args_str}", + f"{python_cmd} -m finecode_extension_runner.cli start {process_args_str}", runner_dir, ) runner_info_instance.client = client @@ -439,7 +439,7 @@ async def check_runner(runner_dir: Path, env_name: str) -> bool: # get version of extension runner. If it works and we get valid # value, assume extension runner works correctly - cmd = f'{python_cmd} -m finecode.extension_runner.cli version' + cmd = f'{python_cmd} -m finecode_extension_runner.cli version' logger.debug(f"Run '{cmd}' in {runner_dir}") async_subprocess = await asyncio.create_subprocess_shell( cmd, diff --git a/src/finecode/workspace_manager/runner/runner_client.py b/src/finecode/runner/runner_client.py similarity index 98% rename from src/finecode/workspace_manager/runner/runner_client.py rename to src/finecode/runner/runner_client.py index 4942e3a..f4c9b40 100644 --- a/src/finecode/workspace_manager/runner/runner_client.py +++ b/src/finecode/runner/runner_client.py @@ -14,10 +14,10 @@ from lsprotocol import types from pygls import exceptions as pygls_exceptions -import finecode.workspace_manager.domain as domain +import finecode.domain as domain if TYPE_CHECKING: - from finecode.workspace_manager.runner.runner_info import ExtensionRunnerInfo + from finecode.runner.runner_info import ExtensionRunnerInfo class BaseRunnerRequestException(Exception): @@ -46,7 +46,8 @@ async def send_request( method: str, params: Any | None, timeout: int | None = 10, -) -> Any | None: +) -> Any: + logger.debug(f"Send {method} to {runner.working_dir_path}") try: response = await asyncio.wait_for( runner.client.protocol.send_request_async( diff --git a/src/finecode/workspace_manager/runner/runner_info.py b/src/finecode/runner/runner_info.py similarity index 98% rename from src/finecode/workspace_manager/runner/runner_info.py rename to src/finecode/runner/runner_info.py index ec81c2a..b644038 100644 --- a/src/finecode/workspace_manager/runner/runner_info.py +++ b/src/finecode/runner/runner_info.py @@ -32,6 +32,7 @@ async def start_io(self, cmd: str, *args, **kwargs): stderr=asyncio.subprocess.PIPE, **kwargs, ) + logger.debug(f"{cmd} - process id: {server.pid}") # Keep mypy happy if server.stdout is None: diff --git a/src/finecode/workspace_manager/services.py b/src/finecode/services.py similarity index 96% rename from src/finecode/workspace_manager/services.py rename to src/finecode/services.py index 134da54..a9b080d 100644 --- a/src/finecode/workspace_manager/services.py +++ b/src/finecode/services.py @@ -4,14 +4,14 @@ from loguru import logger import ordered_set -from finecode.workspace_manager import ( +from finecode import ( context, domain, payload_preprocessor, user_messages, ) -from finecode.workspace_manager.runner import manager as runner_manager, runner_info -from finecode.workspace_manager.runner import runner_client +from finecode.runner import manager as runner_manager, runner_info +from finecode.runner import runner_client async def restart_extension_runners( diff --git a/src/finecode/workspace_manager/user_messages.py b/src/finecode/user_messages.py similarity index 100% rename from src/finecode/workspace_manager/user_messages.py rename to src/finecode/user_messages.py diff --git a/src/finecode/utils/__init__.py b/src/finecode/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/finecode/workspace_manager/utils/async_proc_queue.py b/src/finecode/utils/async_proc_queue.py similarity index 100% rename from src/finecode/workspace_manager/utils/async_proc_queue.py rename to src/finecode/utils/async_proc_queue.py diff --git a/src/finecode/workspace_manager/utils/iterable_subscribe.py b/src/finecode/utils/iterable_subscribe.py similarity index 100% rename from src/finecode/workspace_manager/utils/iterable_subscribe.py rename to src/finecode/utils/iterable_subscribe.py diff --git a/src/finecode/workspace_manager/watch_and_run.py b/src/finecode/watch_and_run.py similarity index 91% rename from src/finecode/workspace_manager/watch_and_run.py rename to src/finecode/watch_and_run.py index 71b3ff2..a5f7586 100644 --- a/src/finecode/workspace_manager/watch_and_run.py +++ b/src/finecode/watch_and_run.py @@ -1,8 +1,8 @@ from loguru import logger -import finecode.workspace_manager.context as context -import finecode.workspace_manager.find_project as find_project -import finecode.workspace_manager.watcher as watcher +import finecode.context as context +import finecode.find_project as find_project +import finecode.watcher as watcher async def watch_and_run( diff --git a/src/finecode/workspace_manager/watcher.py b/src/finecode/watcher.py similarity index 98% rename from src/finecode/workspace_manager/watcher.py rename to src/finecode/watcher.py index cf55747..005e0ff 100644 --- a/src/finecode/workspace_manager/watcher.py +++ b/src/finecode/watcher.py @@ -13,8 +13,8 @@ from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer -import finecode.workspace_manager.context as context -import finecode.workspace_manager.utils.async_proc_queue as async_queue +import finecode.context as context +import finecode.utils.async_proc_queue as async_queue @dataclass From b1f4ba9b2accea3e9d48fee358b875a1c355e554 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Fri, 15 Aug 2025 08:41:30 +0200 Subject: [PATCH 07/46] CLI: start extension runners also if they were running before and were exited or failed --- src/finecode/cli_app/prepare_envs.py | 81 ++++++++++++++-------------- src/finecode/cli_app/run.py | 10 +++- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/src/finecode/cli_app/prepare_envs.py b/src/finecode/cli_app/prepare_envs.py index 4c665ea..c765bf3 100644 --- a/src/finecode/cli_app/prepare_envs.py +++ b/src/finecode/cli_app/prepare_envs.py @@ -43,7 +43,7 @@ async def prepare_envs(workdir_path: pathlib.Path) -> None: try: # try to start runner in 'dev_workspace' env of each project. If venv doesn't # exist or doesn't work, recreate it by running actions in the current env. - await start_or_recreate_all_dev_workspace_envs(projects=projects, workdir_path=workdir_path, ws_context=ws_context) + await check_or_recreate_all_dev_workspace_envs(projects=projects, workdir_path=workdir_path, ws_context=ws_context) # now all 'dev_workspace' envs are valid, run 'prepare_envs' in them to create # envs in each subproject. @@ -59,7 +59,9 @@ async def prepare_envs(workdir_path: pathlib.Path) -> None: services.on_shutdown(ws_context) -async def start_or_recreate_all_dev_workspace_envs(projects: list[domain.Project], workdir_path: pathlib.Path, ws_context: context.WorkspaceContext) -> None: +async def check_or_recreate_all_dev_workspace_envs(projects: list[domain.Project], workdir_path: pathlib.Path, ws_context: context.WorkspaceContext) -> None: + # NOTE: this function can start new extensions runner, don't forget to call + # on_shutdown if you use it projects_dirs_with_valid_envs: list[pathlib.Path] = [] projects_dirs_with_invalid_envs: list[pathlib.Path] = [] @@ -88,46 +90,43 @@ async def start_or_recreate_all_dev_workspace_envs(projects: list[domain.Project # TODO raise exception - try: - envs = [] + envs = [] + + # run pip install in dev_workspace even if env exists to make sure that correct + # dependencies are installed + for project_dir_path in projects_dirs_with_valid_envs: + if project_dir_path == workdir_path: + # skip installation of dependencies in `dev_workspace` env of the + # current project, because user is responsible for keeping them + # up-to-date + continue - # run pip install in dev_workspace even if env exists to make sure that correct - # dependencies are installed - for project_dir_path in projects_dirs_with_valid_envs: - if project_dir_path == workdir_path: - # skip installation of dependencies in `dev_workspace` env of the - # current project, because user is responsible for keeping them - # up-to-date - continue - - # dependencies in `dev_workspace` should be simple and installable without - # dumping - envs.append({"name": "dev_workspace", "venv_dir_path": project_dir_path / '.venvs' / 'dev_workspace', "project_def_path": project_dir_path / 'pyproject.toml' }) + # dependencies in `dev_workspace` should be simple and installable without + # dumping + envs.append({"name": "dev_workspace", "venv_dir_path": project_dir_path / '.venvs' / 'dev_workspace', "project_def_path": project_dir_path / 'pyproject.toml' }) - if len(projects_dirs_with_invalid_envs) > 0: - invalid_envs = [] + if len(projects_dirs_with_invalid_envs) > 0: + invalid_envs = [] - for project_dir_path in projects_dirs_with_invalid_envs: - # dependencies in `dev_workspace` should be simple and installable without - # dumping - invalid_envs.append({"name": "dev_workspace", "venv_dir_path": project_dir_path / '.venvs' / 'dev_workspace', "project_def_path": project_dir_path / 'pyproject.toml' }) - - # remove existing invalid envs - for env_info in invalid_envs: - if env_info["venv_dir_path"].exists(): - logger.trace(f"{env_info['venv_dir_path']} was invalid, remove it") - shutil.rmtree(env_info["venv_dir_path"]) - - envs += invalid_envs + for project_dir_path in projects_dirs_with_invalid_envs: + # dependencies in `dev_workspace` should be simple and installable without + # dumping + invalid_envs.append({"name": "dev_workspace", "venv_dir_path": project_dir_path / '.venvs' / 'dev_workspace', "project_def_path": project_dir_path / 'pyproject.toml' }) + + # remove existing invalid envs + for env_info in invalid_envs: + if env_info["venv_dir_path"].exists(): + logger.trace(f"{env_info['venv_dir_path']} was invalid, remove it") + shutil.rmtree(env_info["venv_dir_path"]) + + envs += invalid_envs - # TODO: check result - await services.run_action( - action_name='prepare_dev_workspaces_envs', - params={ "envs": envs, }, - project_def=current_project, - ws_context=ws_context, - result_format=services.RunResultFormat.STRING, - preprocess_payload=False - ) - finally: - runner_manager.stop_extension_runner_sync(runner) + # TODO: check result + await services.run_action( + action_name='prepare_dev_workspaces_envs', + params={ "envs": envs, }, + project_def=current_project, + ws_context=ws_context, + result_format=services.RunResultFormat.STRING, + preprocess_payload=False + ) diff --git a/src/finecode/cli_app/run.py b/src/finecode/cli_app/run.py index a72c257..1153930 100644 --- a/src/finecode/cli_app/run.py +++ b/src/finecode/cli_app/run.py @@ -8,7 +8,7 @@ from finecode import context, domain, services from finecode.config import read_configs, collect_actions -from finecode.runner import manager as runner_manager +from finecode.runner import manager as runner_manager, runner_info class RunFailed(Exception): @@ -40,7 +40,13 @@ async def start_required_environments( existing_runners = ws_context.ws_projects_extension_runners.get(project_dir_path, {}) for env_name in required_envs: - if env_name not in existing_runners: + runner_exist = env_name in existing_runners + start_runner = True + if runner_exist: + runner_is_running = existing_runners[env_name].status == runner_info.RunnerStatus.RUNNING + start_runner = not runner_is_running + + if start_runner: try: runner = await runner_manager.start_runner( project_def=project, From 790c37afd88efd59bbfdf0d6ac98854af943d9f9 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Fri, 15 Aug 2025 14:47:21 +0200 Subject: [PATCH 08/46] Add errors parameter to result of prepare_env action. Extend file manager interface with `remove_dir` method. Adapt extensions to these changes --- .../fine_python_pip/prepare_env_handler.py | 7 ++++--- .../src/fine_python_virtualenv/handler.py | 10 ++++++++-- .../actions/prepare_envs.py | 20 +++++++++++++++---- .../interfaces/ifilemanager.py | 5 ++++- .../prepare_envs_dump_configs.py | 2 +- .../impls/file_manager.py | 4 ++++ src/finecode/cli.py | 3 ++- src/finecode/cli_app/prepare_envs.py | 4 ++-- 8 files changed, 41 insertions(+), 14 deletions(-) diff --git a/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py b/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py index 7b9b1a9..35ab359 100644 --- a/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py +++ b/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py @@ -38,10 +38,11 @@ async def run( for process in install_processes: tg.create_task(process.wait_for_end()) + errors: list[str] = [] for idx, process in enumerate(install_processes): if process.get_exit_code() != 0: env_info = payload.envs[idx] project_def_path = run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] - self.logger.error(f'Installation of dependencies in env {env_info.name} from {project_def_path} failed:\nstdout: {process.get_output()}\nstderr: {process.get_error_output()}') - # TODO: return code, return logs? - return prepare_envs_action.PrepareEnvsRunResult(results=[env_info.venv_dir_path for env_info in payload.envs]) + errors.append(f'Installation of dependencies in env {env_info.name} from {project_def_path} failed:\nstdout: {process.get_output()}\nstderr: {process.get_error_output()}') + + return prepare_envs_action.PrepareEnvsRunResult(errors=errors) diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py index 767dd10..e5cf5de 100644 --- a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py @@ -1,4 +1,4 @@ -from finecode_extension_api.interfaces import ilogger +from finecode_extension_api.interfaces import ilogger, ifilemanager import virtualenv from finecode_extension_api import code_action @@ -16,9 +16,11 @@ def __init__( self, config: VirtualenvPrepareEnvHandlerConfig, logger: ilogger.ILogger, + file_manager: ifilemanager.IFileManager ) -> None: self.config = config self.logger = logger + self.file_manager = file_manager async def run( self, @@ -29,6 +31,10 @@ async def run( # would it be faster parallel? for env_info in payload.envs: + if payload.recreate and env_info.venv_dir_path.exists(): + self.logger.debug(f"Remove virtualenv dir {env_info.venv_dir_path}") + self.file_manager.remove_dir(env_info.venv_dir_path) + self.logger.info(f"Creating virtualenv {env_info.venv_dir_path}") if not env_info.venv_dir_path.exists(): # TODO: '-p ' @@ -36,4 +42,4 @@ async def run( else: self.logger.info(f"Virtualenv in {env_info} exists already") - return prepare_envs_action.PrepareEnvsRunResult(results=[env_info.venv_dir_path for env_info in payload.envs]) + return prepare_envs_action.PrepareEnvsRunResult(errors=[]) diff --git a/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py b/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py index d52708f..4cf2d29 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py @@ -20,6 +20,11 @@ class EnvInfo: @dataclasses.dataclass class PrepareEnvsRunPayload(code_action.RunActionPayload): envs: list[EnvInfo] + # remove old env and create a new one from scratch even if the current one is valid. + # Useful for example if you changed something in venv manually and want to revert + # changes (just by running prepare it would be not solved because version of the + # packages are the same and they are already installed) + recreate: bool = False class PrepareEnvsRunContext(code_action.RunActionContext): @@ -43,17 +48,24 @@ async def init(self, initial_payload: PrepareEnvsRunPayload) -> None: @dataclasses.dataclass class PrepareEnvsRunResult(code_action.RunActionResult): - # TODO: statuses, errors, logs? - # TODO: return code property - results: list[pathlib.Path] + # `PrepareEnvs` action is general, so make result general as well + errors: list[str] @override def update(self, other: code_action.RunActionResult) -> None: if not isinstance(other, PrepareEnvsRunResult): return + self.errors += other.errors def to_text(self) -> str | textstyler.StyledText: - return '' + return '\n'.join(self.errors) + + @property + def return_code(self) -> code_action.RunReturnCode: + if len(self.errors) == 0: + return code_action.RunReturnCode.SUCCESS + else: + return code_action.RunReturnCode.ERROR class PrepareEnvsAction(code_action.Action): diff --git a/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py b/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py index 95635d7..2f8cdc4 100644 --- a/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py +++ b/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py @@ -11,5 +11,8 @@ async def get_file_version(self, file_path: Path) -> str: async def save_file(self, file_path: Path, file_content) -> None: ... - async def create_dir(self, dir_path: Path, create_parents: bool = True, exist_ok: bool = True): + async def create_dir(self, dir_path: Path, create_parents: bool = True, exist_ok: bool = True) -> None: + ... + + async def remove_dir(self, dir_path: Path) -> None: ... \ No newline at end of file diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py index c67045a..23afbe5 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py @@ -73,4 +73,4 @@ async def run( new_item_path.unlink() new_item_path.symlink_to(item, target_is_directory=item.is_dir()) - return prepare_envs_action.PrepareEnvsRunResult(results=[]) # TODO + return prepare_envs_action.PrepareEnvsRunResult(errors=[]) diff --git a/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py b/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py index d9d872e..3dae9a1 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py +++ b/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py @@ -1,4 +1,5 @@ import hashlib +import shutil from pathlib import Path from typing import Callable @@ -78,6 +79,9 @@ async def create_dir(self, dir_path: Path, create_parents: bool = True, exist_ok # currently only local file system is supported dir_path.mkdir(parents=create_parents, exist_ok=exist_ok) + async def remove_dir(self, dir_path: Path) -> None: + shutil.rmtree(dir_path) + # helper methods def read_content_file_from_fs(self, file_path: Path) -> str: # don't use this method directly, use `get_content` instead diff --git a/src/finecode/cli.py b/src/finecode/cli.py index dbdbc50..e6e7791 100644 --- a/src/finecode/cli.py +++ b/src/finecode/cli.py @@ -177,8 +177,9 @@ def run(ctx) -> None: @cli.command() @click.option("--trace", "trace", is_flag=True, default=False) @click.option("--debug", "debug", is_flag=True, default=False) +@click.option("--recreate", "recreate", is_flag=True, default=False) def prepare_envs(trace: bool, - debug: bool) -> None: + debug: bool, recreate: bool) -> None: """ `prepare-envs` should be called from workspace/project root directory. """ diff --git a/src/finecode/cli_app/prepare_envs.py b/src/finecode/cli_app/prepare_envs.py index c765bf3..48644a9 100644 --- a/src/finecode/cli_app/prepare_envs.py +++ b/src/finecode/cli_app/prepare_envs.py @@ -8,7 +8,7 @@ from finecode.runner import manager as runner_manager -async def prepare_envs(workdir_path: pathlib.Path) -> None: +async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: # similar to `run_actions`, but with certain differences: # - prepare_envs doesn't support presets because `dev_no_runtime` env most # probably doesn't exist yet @@ -38,7 +38,7 @@ async def prepare_envs(workdir_path: pathlib.Path) -> None: actions_by_projects: dict[pathlib.Path, list[str]] = {project.dir_path: ['prepare_envs'] for project in projects} # action payload can be kept empty because it will be filled in payload preprocessor - action_payload: dict[str, str] = {} + action_payload: dict[str, str | bool] = {"recreate": recreate} try: # try to start runner in 'dev_workspace' env of each project. If venv doesn't From d15a76e400bbb8d1cf00e3d9a287da1545e82416 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 21 Aug 2025 16:04:26 +0200 Subject: [PATCH 09/46] Improve error handling. Add base_config. Add prepare_runners and install_deps_in_env actions and handlers. --- extensions/fine_python_pip/pyproject.toml | 6 +- extensions/fine_python_pip/setup.py | 58 +++++ .../src/fine_python_pip/__init__.py | 5 +- .../install_deps_in_env_handler.py | 92 ++++++++ .../fine_python_virtualenv/pyproject.toml | 6 +- extensions/fine_python_virtualenv/setup.py | 58 +++++ .../src/fine_python_virtualenv/__init__.py | 6 +- .../{handler.py => prepare_envs_handler.py} | 0 .../prepare_runners_handler.py | 45 ++++ finecode_extension_api/pyproject.toml | 4 +- finecode_extension_api/setup.py | 58 +++++ .../actions/dump_config.py | 5 +- .../actions/install_deps_in_env.py | 60 ++++++ .../actions/prepare_envs.py | 7 + .../actions/prepare_runners.py | 81 +++++++ .../src/finecode_extension_api/code_action.py | 10 + .../interfaces/iactionrunner.py | 3 +- finecode_extension_runner/pyproject.toml | 22 +- finecode_extension_runner/setup.py | 58 +++++ .../_services/run_action.py | 49 +++-- .../action_handlers/__init__.py | 8 + .../action_handlers/dump_config.py | 42 +++- .../action_handlers/dump_config_save.py | 2 +- .../prepare_envs_dump_configs.py | 41 ++-- .../prepare_envs_install_deps.py | 76 +++++++ .../prepare_envs_read_configs.py | 39 ++++ .../prepare_runners_dump_configs.py | 47 ++++ ...pare_runners_install_runner_and_presets.py | 90 ++++++++ .../src/finecode_extension_runner/cli.py | 9 +- .../finecode_extension_runner/di/bootstrap.py | 3 +- .../impls/process_executor.py | 1 - .../finecode_extension_runner/lsp_server.py | 20 +- .../src/finecode_extension_runner/services.py | 2 +- presets/fine_python_format/pyproject.toml | 16 +- presets/fine_python_format/setup.py | 58 +++++ presets/fine_python_lint/setup.py | 58 +++++ presets/fine_python_recommended/setup.py | 58 +++++ setup.py | 58 +++++ src/finecode/base_config.toml | 60 ++++++ src/finecode/cli.py | 11 +- src/finecode/cli_app/prepare_envs.py | 102 +++++++-- src/finecode/cli_app/run.py | 55 ++++- src/finecode/config/collect_actions.py | 8 +- src/finecode/config/config_models.py | 7 +- src/finecode/config/read_configs.py | 203 ++++++------------ src/finecode/finecode_cmd.py | 8 +- src/finecode/payload_preprocessor.py | 2 +- src/finecode/runner/manager.py | 33 ++- src/finecode/runner/runner_client.py | 6 + src/finecode/services.py | 6 +- 50 files changed, 1496 insertions(+), 266 deletions(-) create mode 100644 extensions/fine_python_pip/setup.py create mode 100644 extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py create mode 100644 extensions/fine_python_virtualenv/setup.py rename extensions/fine_python_virtualenv/src/fine_python_virtualenv/{handler.py => prepare_envs_handler.py} (100%) create mode 100644 extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py create mode 100644 finecode_extension_api/setup.py create mode 100644 finecode_extension_api/src/finecode_extension_api/actions/install_deps_in_env.py create mode 100644 finecode_extension_api/src/finecode_extension_api/actions/prepare_runners.py create mode 100644 finecode_extension_runner/setup.py create mode 100644 finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py create mode 100644 finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py create mode 100644 finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py create mode 100644 finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py create mode 100644 presets/fine_python_format/setup.py create mode 100644 presets/fine_python_lint/setup.py create mode 100644 presets/fine_python_recommended/setup.py create mode 100644 setup.py create mode 100644 src/finecode/base_config.toml diff --git a/extensions/fine_python_pip/pyproject.toml b/extensions/fine_python_pip/pyproject.toml index e534e68..423a57c 100644 --- a/extensions/fine_python_pip/pyproject.toml +++ b/extensions/fine_python_pip/pyproject.toml @@ -1,14 +1,14 @@ [project] name = "fine-python-pip" -version = "0.1.0" +version = "0.1.1" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.0"] +dependencies = ["finecode_extension_api==0.1.3"] [dependency-groups] -dev_workspace = ["finecode==0.2.*"] +dev_workspace = ["finecode==0.2.6.dev14+g790c37afd.d20250821"] dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] [tool.finecode] diff --git a/extensions/fine_python_pip/setup.py b/extensions/fine_python_pip/setup.py new file mode 100644 index 0000000..c9c1924 --- /dev/null +++ b/extensions/fine_python_pip/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="finecode_extension_runner", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/extensions/fine_python_pip/src/fine_python_pip/__init__.py b/extensions/fine_python_pip/src/fine_python_pip/__init__.py index 244480c..bd2050f 100644 --- a/extensions/fine_python_pip/src/fine_python_pip/__init__.py +++ b/extensions/fine_python_pip/src/fine_python_pip/__init__.py @@ -1,5 +1,8 @@ +# TODO: is not used anymore from .prepare_env_handler import PipPrepareEnvHandler +from .install_deps_in_env_handler import PipInstallDepsInEnvHandler __all__ = [ - 'PipPrepareEnvHandler' + 'PipPrepareEnvHandler', + 'PipInstallDepsInEnvHandler' ] \ No newline at end of file diff --git a/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py b/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py new file mode 100644 index 0000000..5fa63d9 --- /dev/null +++ b/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py @@ -0,0 +1,92 @@ +import asyncio +import dataclasses +import pathlib + +from finecode_extension_api import code_action +from finecode_extension_api.actions import install_deps_in_env as install_deps_in_env_action +from finecode_extension_api.interfaces import (icommandrunner, ilogger) + + +@dataclasses.dataclass +class PipInstallDepsInEnvHandlerConfig(code_action.ActionHandlerConfig): + find_links: list[str] | None = None + + +class PipInstallDepsInEnvHandler( + code_action.ActionHandler[install_deps_in_env_action.InstallDepsInEnvAction, PipInstallDepsInEnvHandlerConfig] +): + def __init__(self, config: PipInstallDepsInEnvHandlerConfig, command_runner: icommandrunner.ICommandRunner, logger: ilogger.ILogger) -> None: + self.config = config + self.command_runner = command_runner + self.logger = logger + + async def run( + self, + payload: install_deps_in_env_action.InstallDepsInEnvRunPayload, + run_context: install_deps_in_env_action.InstallDepsInEnvRunContext, + ) -> install_deps_in_env_action.InstallDepsInEnvRunResult: + env_name = payload.env_name + dependencies = payload.dependencies + venv_dir_path = payload.venv_dir_path + project_dir_path = payload.project_dir_path + python_executable = venv_dir_path / 'bin' / 'python' + + # split dependencies in editable and not editable because pip supports + # installation of editable only with CLI flag '-e' + editable_dependencies: list[install_deps_in_env_action.Dependency] = [] + non_editable_dependencies: list[install_deps_in_env_action.Dependency] = [] + for dependency in dependencies: + if dependency.editable: + editable_dependencies.append(dependency) + else: + non_editable_dependencies.append(dependency) + + errors: list[str] = [] + # run pip processes sequentially because they are executed in the same venv, + # avoid potential concurrency problem in this way + if len(non_editable_dependencies) > 0: + cmd = self._construct_pip_install_cmd(python_executable=python_executable, dependencies=non_editable_dependencies, editable=False) + error = await self._run_pip_cmd(cmd=cmd, env_name=env_name, project_dir_path=project_dir_path) + if error is not None: + errors.append(error) + + # install editable after non-editable, because non-editable can overwrite editable if there is the same dependency + if len(editable_dependencies) > 0: + cmd = self._construct_pip_install_cmd(python_executable=python_executable, dependencies=editable_dependencies, editable=True) + error = await self._run_pip_cmd(cmd=cmd, env_name=env_name, project_dir_path=project_dir_path) + if error is not None: + errors.append(error) + + return install_deps_in_env_action.InstallDepsInEnvRunResult(errors=errors) + + def _construct_pip_install_cmd(self, python_executable: pathlib.Path, dependencies: list[install_deps_in_env_action.Dependency], editable: bool) -> str: + install_params: str = '' + if editable: + install_params += '-e ' + + if self.config.find_links is not None: + for link in self.config.find_links: + install_params += f' --find-links="{link}"' + + for dependency in dependencies: + if '@ file://' in dependency.version_or_source: + # dependency is specified as ' @ file://' but pip CLI supports + # only 'file://' + start_idx_of_file_uri = dependency.version_or_source.index('file://') + # put in single quoutes to avoid problems in case of spaces in path + # because in CLI commands single dependencies are splitted by space + install_params += f"'{dependency.version_or_source[start_idx_of_file_uri:]}' " + else: + # put in single quoutes to avoid problems in case of spaces in version, + # because in CLI commands single dependencies are splitted by space + install_params += f"'{dependency.name}{dependency.version_or_source}' " + cmd = f'{python_executable} -m pip --disable-pip-version-check install {install_params}' + return cmd + + async def _run_pip_cmd(self, cmd: str, env_name: str, project_dir_path: pathlib.Path) -> str | None: + process = await self.command_runner.run(cmd, cwd=project_dir_path) + await process.wait_for_end() + if process.get_exit_code() != 0: + return f'Installation of dependencies "{cmd}" in env {env_name} from {project_dir_path} failed:\nstdout: {process.get_output()}\nstderr: {process.get_error_output()}' + + return None \ No newline at end of file diff --git a/extensions/fine_python_virtualenv/pyproject.toml b/extensions/fine_python_virtualenv/pyproject.toml index ef3e83d..0876960 100644 --- a/extensions/fine_python_virtualenv/pyproject.toml +++ b/extensions/fine_python_virtualenv/pyproject.toml @@ -1,17 +1,17 @@ [project] name = "fine-python-virtualenv" -version = "0.1.0" +version = "0.1.2" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" dependencies = [ - "finecode_extension_api==0.1.0", + "finecode_extension_api==0.1.3", "virtualenv (>=20.0.0,<21.0.0)", ] [dependency-groups] -dev_workspace = ["finecode==0.2.*"] +dev_workspace = ["finecode==0.2.6.dev14+g790c37afd.d20250821"] dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] [tool.finecode] diff --git a/extensions/fine_python_virtualenv/setup.py b/extensions/fine_python_virtualenv/setup.py new file mode 100644 index 0000000..c9c1924 --- /dev/null +++ b/extensions/fine_python_virtualenv/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="finecode_extension_runner", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py index 1aa1091..cec0c11 100644 --- a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py @@ -1,6 +1,8 @@ -from .handler import VirtualenvPrepareEnvHandler +from .prepare_envs_handler import VirtualenvPrepareEnvHandler +from .prepare_runners_handler import VirtualenvPrepareRunnersHandler __all__ = [ - 'VirtualenvPrepareEnvHandler' + 'VirtualenvPrepareEnvHandler', + 'VirtualenvPrepareRunnersHandler' ] diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py similarity index 100% rename from extensions/fine_python_virtualenv/src/fine_python_virtualenv/handler.py rename to extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py new file mode 100644 index 0000000..c00f9c8 --- /dev/null +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py @@ -0,0 +1,45 @@ +from finecode_extension_api.interfaces import ilogger, ifilemanager +import virtualenv + +from finecode_extension_api import code_action +from finecode_extension_api.actions import prepare_runners as prepare_runners_action + + +class VirtualenvPrepareRunnersHandlerConfig(code_action.ActionHandlerConfig): + ... + + +class VirtualenvPrepareRunnersHandler( + code_action.ActionHandler[prepare_runners_action.PrepareRunnersAction, VirtualenvPrepareRunnersHandlerConfig] +): + def __init__( + self, + config: VirtualenvPrepareRunnersHandlerConfig, + logger: ilogger.ILogger, + file_manager: ifilemanager.IFileManager + ) -> None: + self.config = config + self.logger = logger + self.file_manager = file_manager + + async def run( + self, + payload: prepare_runners_action.PrepareRunnersRunPayload, + run_context: prepare_runners_action.PrepareRunnersRunContext, + ) -> prepare_runners_action.PrepareRunnersRunResult: + # create virtual envs + + # would it be faster parallel? + for env_info in payload.envs: + if payload.recreate and env_info.venv_dir_path.exists(): + self.logger.debug(f"Remove virtualenv dir {env_info.venv_dir_path}") + await self.file_manager.remove_dir(env_info.venv_dir_path) + + self.logger.info(f"Creating virtualenv {env_info.venv_dir_path}") + if not env_info.venv_dir_path.exists(): + # TODO: '-p ' + virtualenv.cli_run([env_info.venv_dir_path.as_posix()], options=None, setup_logging=False, env=None) + else: + self.logger.info(f"Virtualenv in {env_info} exists already") + + return prepare_runners_action.PrepareRunnersRunResult(errors=[]) diff --git a/finecode_extension_api/pyproject.toml b/finecode_extension_api/pyproject.toml index a80c3c3..8484621 100644 --- a/finecode_extension_api/pyproject.toml +++ b/finecode_extension_api/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "finecode-extension-api" -version = "0.1.0" +version = "0.1.3" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" @@ -8,7 +8,7 @@ requires-python = ">=3.11, < 3.14" dependencies = ["typing-extensions (>=4.12.2,<5.0.0)"] [dependency-groups] -dev_workspace = ["finecode==0.2.*"] +dev_workspace = ["finecode==0.2.6.dev14+g790c37afd.d20250821"] dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] [tool.finecode] diff --git a/finecode_extension_api/setup.py b/finecode_extension_api/setup.py new file mode 100644 index 0000000..c7e4537 --- /dev/null +++ b/finecode_extension_api/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="finecode_extension_api", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py b/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py index b150e77..bfd70f8 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py @@ -36,14 +36,15 @@ async def init(self, initial_payload: DumpConfigRunPayload) -> None: @dataclasses.dataclass class DumpConfigRunResult(code_action.RunActionResult): - # TODO: return dumped config as result, not only save in file - ... + config_dump: dict[str, typing.Any] @override def update(self, other: code_action.RunActionResult) -> None: if not isinstance(other, DumpConfigRunResult): return + self.config_dump = other.config_dump + def to_text(self) -> str | textstyler.StyledText: return '' diff --git a/finecode_extension_api/src/finecode_extension_api/actions/install_deps_in_env.py b/finecode_extension_api/src/finecode_extension_api/actions/install_deps_in_env.py new file mode 100644 index 0000000..151f56a --- /dev/null +++ b/finecode_extension_api/src/finecode_extension_api/actions/install_deps_in_env.py @@ -0,0 +1,60 @@ +import dataclasses +import pathlib +import sys + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +from finecode_extension_api import code_action, textstyler + + +@dataclasses.dataclass +class Dependency: + name: str + version_or_source: str + editable: bool = False + + +@dataclasses.dataclass +class InstallDepsInEnvRunPayload(code_action.RunActionPayload): + env_name: str + venv_dir_path: pathlib.Path + project_dir_path: pathlib.Path + dependencies: list[Dependency] + + +class InstallDepsInEnvRunContext(code_action.RunActionContext): + def __init__( + self, + run_id: int, + ) -> None: + super().__init__(run_id=run_id) + + +@dataclasses.dataclass +class InstallDepsInEnvRunResult(code_action.RunActionResult): + errors: list[str] + + @override + def update(self, other: code_action.RunActionResult) -> None: + if not isinstance(other, InstallDepsInEnvRunResult): + return + self.errors += other.errors + + def to_text(self) -> str | textstyler.StyledText: + return '\n'.join(self.errors) + + @property + def return_code(self) -> code_action.RunReturnCode: + if len(self.errors) == 0: + return code_action.RunReturnCode.SUCCESS + else: + return code_action.RunReturnCode.ERROR + + +class InstallDepsInEnvAction(code_action.Action): + PAYLOAD_TYPE = InstallDepsInEnvRunPayload + RUN_CONTEXT_TYPE = InstallDepsInEnvRunContext + RESULT_TYPE = InstallDepsInEnvRunResult diff --git a/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py b/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py index 4cf2d29..15ebe60 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py @@ -1,6 +1,7 @@ import dataclasses import pathlib import sys +import typing if sys.version_info >= (3, 12): from typing import override @@ -40,6 +41,12 @@ def __init__( # normalizing(dumping) configuration first and then use dumped config for # further handlers. self.project_def_path_by_venv_dir_path: dict[pathlib.Path, pathlib.Path] = {} + # to avoid multiple writing and reading files in each action handler, save + # modified project definition here. It also can be used as extension point if + # for example additional dependencies should be installed by adding handler + # which inserts them into project definition instead of modying `install_deps` + # handler + self.project_def_by_venv_dir_path: dict[pathlib.Path, dict[str, typing.Any]] = {} async def init(self, initial_payload: PrepareEnvsRunPayload) -> None: for env_info in initial_payload.envs: diff --git a/finecode_extension_api/src/finecode_extension_api/actions/prepare_runners.py b/finecode_extension_api/src/finecode_extension_api/actions/prepare_runners.py new file mode 100644 index 0000000..154c802 --- /dev/null +++ b/finecode_extension_api/src/finecode_extension_api/actions/prepare_runners.py @@ -0,0 +1,81 @@ +import dataclasses +import pathlib +import sys +import typing + +if sys.version_info >= (3, 12): + from typing import override +else: + from typing_extensions import override + +from finecode_extension_api import code_action, textstyler + + +@dataclasses.dataclass +class EnvInfo: + name: str + venv_dir_path: pathlib.Path + project_def_path: pathlib.Path + + +@dataclasses.dataclass +class PrepareRunnersRunPayload(code_action.RunActionPayload): + envs: list[EnvInfo] + # remove old env and create a new one from scratch even if the current one is valid. + # Useful for example if you changed something in venv manually and want to revert + # changes (just by running prepare it would be not solved because version of the + # packages are the same and they are already installed) + recreate: bool = False + + +class PrepareRunnersRunContext(code_action.RunActionContext): + def __init__( + self, + run_id: int, + ) -> None: + super().__init__(run_id=run_id) + + # project def pathes are stored also in context, because prepare envs can run + # tools like pip which expected 'normalized' project definition(=without + # additional features which finecode provides). So the usual workflow looks like + # normalizing(dumping) configuration first and then use dumped config for + # further handlers. + self.project_def_path_by_venv_dir_path: dict[pathlib.Path, pathlib.Path] = {} + # to avoid multiple writing and reading files in each action handler, save + # modified project definition here. It also can be used as extension point if + # for example additional dependencies should be installed by adding handler + # which inserts them into project definition instead of modying `install_deps` + # handler + self.project_def_by_venv_dir_path: dict[pathlib.Path, dict[str, typing.Any]] = {} + + async def init(self, initial_payload: PrepareRunnersRunPayload) -> None: + for env_info in initial_payload.envs: + self.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = env_info.project_def_path + + +@dataclasses.dataclass +class PrepareRunnersRunResult(code_action.RunActionResult): + # `PrepareRunners` action is general, so make result general as well + errors: list[str] + + @override + def update(self, other: code_action.RunActionResult) -> None: + if not isinstance(other, PrepareRunnersRunResult): + return + self.errors += other.errors + + def to_text(self) -> str | textstyler.StyledText: + return '\n'.join(self.errors) + + @property + def return_code(self) -> code_action.RunReturnCode: + if len(self.errors) == 0: + return code_action.RunReturnCode.SUCCESS + else: + return code_action.RunReturnCode.ERROR + + +class PrepareRunnersAction(code_action.Action): + PAYLOAD_TYPE = PrepareRunnersRunPayload + RUN_CONTEXT_TYPE = PrepareRunnersRunContext + RESULT_TYPE = PrepareRunnersRunResult diff --git a/finecode_extension_api/src/finecode_extension_api/code_action.py b/finecode_extension_api/src/finecode_extension_api/code_action.py index 25e4261..55c4d45 100644 --- a/finecode_extension_api/src/finecode_extension_api/code_action.py +++ b/finecode_extension_api/src/finecode_extension_api/code_action.py @@ -93,6 +93,16 @@ class Action(Generic[RunPayloadType, RunContextType, RunResultType]): CONFIG: typing.Type[ActionConfig] = ActionConfig +class StopActionRunWithResult(Exception): + def __init__(self, result: RunActionResult) -> None: + self.result = result + + +class ActionFailedException(Exception): + def __init__(self, message: str) -> None: + self.message = message + + InitializeCallable = collections.abc.Callable[[], None] ShutdownCallable = collections.abc.Callable[[], None] ExitCallable = collections.abc.Callable[[], None] diff --git a/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py b/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py index afa5478..7b896b8 100644 --- a/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py +++ b/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py @@ -9,7 +9,8 @@ async def run_action( class BaseRunActionException(Exception): - ... + def __init__(self, message: str) -> None: + self.message = message class ActionNotFound(BaseRunActionException): diff --git a/finecode_extension_runner/pyproject.toml b/finecode_extension_runner/pyproject.toml index d4d84eb..cb6984e 100644 --- a/finecode_extension_runner/pyproject.toml +++ b/finecode_extension_runner/pyproject.toml @@ -12,29 +12,27 @@ dependencies = [ "pydantic==2.10.*", "platformdirs==4.3.*", "pygls==2.0.0-a2", - "finecode_extension_api @ file:///home/user/Development/FineCode/finecode/finecode_extension_api/dist/finecode_extension_api-0.1.1-py3-none-any.whl", - "ordered-set==4.1.*", - "fine_python_virtualenv @ file:///home/user/Development/FineCode/finecode/extensions/fine_python_virtualenv/dist/fine_python_virtualenv-0.1.0-py3-none-any.whl", - "fine_python_pip @ file:///home/user/Development/FineCode/finecode/extensions/fine_python_pip/dist/fine_python_pip-0.1.0-py3-none-any.whl", + "finecode_extension_api==0.1.3", ] [dependency-groups] dev_workspace = [ "build==1.2.2.post1", - "finecode_extension_runner @ file:///home/user/Development/FineCode/finecode/finecode_extension_runner", + "finecode==0.2.6.dev14+g790c37afd.d20250821", ] dev = [{ include-group = "runtime" }, "pytest==7.4.*", "debugpy==1.8.*"] dev_no_runtime = [ - "fine_python_import_linter @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_import_linter", + "finecode_dev_common_preset==0.1.*", + # file_python_import_linter is temporary disabled, because it isn't ported to the new finecode_extension_api yet + # "fine_python_import_linter @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_import_linter", ] +[tool.finecode.env.dev_no_runtime.dependencies] +finecode_dev_common_preset = { path = "../finecode_dev_common_preset", editable = true } + [build-system] -requires = [ - "setuptools>=64", - "setuptools-scm>=8", - "finecode_build_backend @ file:///home/user/Development/FineCode/finecode/finecode_build_backend", -] -build-backend = "finecode_build_backend.backend" +requires = ["setuptools>=64", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" [tool.poetry] packages = [{ include = "finecode_extension_runner", from = "src" }] diff --git a/finecode_extension_runner/setup.py b/finecode_extension_runner/setup.py new file mode 100644 index 0000000..c9c1924 --- /dev/null +++ b/finecode_extension_runner/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="finecode_extension_runner", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py index b1ae74f..14eb299 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py +++ b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py @@ -10,6 +10,7 @@ from finecode_extension_runner import project_dirs, run_utils, schemas, global_state, domain, context from finecode_extension_api import code_action, textstyler +from finecode_extension_api.interfaces import iactionrunner from finecode_extension_runner import ( partial_result_sender as partial_result_sender_module, ) @@ -20,7 +21,9 @@ partial_result_sender: partial_result_sender_module.PartialResultSender -class ActionFailedException(Exception): ... +class ActionFailedException(Exception): + def __init__(self, message: str) -> None: + self.message = message def set_partial_result_sender(send_func: typing.Callable) -> None: @@ -167,11 +170,15 @@ async def run_action( subresult_task = tg.create_task(coro) subresults_tasks.append(subresult_task) except ExceptionGroup as eg: - logger.error(eg) + errors: list[str] = [] for exc in eg.exceptions: - logger.exception(exc) + if not isinstance(exc, ActionFailedException): + logger.error("Unexpected exception:") + logger.exception(exc) + else: + errors.append(exc.message) raise ActionFailedException( - f"Running action handlers of '{action.name}' failed(Run {run_id})." + f"Running action handlers of '{action.name}' failed(Run {run_id}): {errors}." " See ER logs for more details" ) @@ -207,8 +214,8 @@ async def run_action( ) handlers_tasks.append(handler_task) except ExceptionGroup as eg: - logger.error(eg) for exc in eg.exceptions: + # TODO: expected / unexpected? logger.exception(exc) raise ActionFailedException( f"Running action handlers of '{action.name}' failed" @@ -224,15 +231,18 @@ async def run_action( action_result.update(coro_result) else: for handler in action.handlers: - handler_result = await execute_action_handler( - handler=handler, - payload=payload, - run_context=run_context, - run_id=run_id, - action_context=action_context, - action_exec_info=action_exec_info, - runner_context=runner_context, - ) + try: + handler_result = await execute_action_handler( + handler=handler, + payload=payload, + run_context=run_context, + run_id=run_id, + action_context=action_context, + action_exec_info=action_exec_info, + runner_context=runner_context, + ) + except ActionFailedException as exception: + raise exception if handler_result is not None: if action_result is None: @@ -453,10 +463,15 @@ def get_run_context(param_type): execution_result = await call_result else: execution_result = call_result - except Exception as e: - logger.exception(e) + except Exception as exception: + if isinstance(exception, iactionrunner.BaseRunActionException) or isinstance(exception, code_action.ActionFailedException): + error_str = exception.message + else: + logger.error("Unhandled exception in action handler:") + logger.exception(exception) + error_str = str(exception) raise ActionFailedException( - f"Running action handler '{handler.name}' failed(Run {run_id}): {e}" + f"Running action handler '{handler.name}' failed(Run {run_id}): {error_str}" ) end_time = time.time_ns() diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py index 857436f..94f768f 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py @@ -1,9 +1,17 @@ from .dump_config import DumpConfigHandler from .prepare_envs_dump_configs import PrepareEnvsDumpConfigsHandler +from .prepare_envs_install_deps import PrepareEnvsInstallDepsHandler +from .prepare_envs_read_configs import PrepareEnvsReadConfigsHandler +from .prepare_runners_dump_configs import PrepareRunnersDumpConfigsHandler +from .prepare_runners_install_runner_and_presets import PrepareRunnersInstallRunnerAndPresetsHandler from .dump_config_save import DumpConfigSaveHandler __all__ = [ 'DumpConfigHandler', 'PrepareEnvsDumpConfigsHandler', + 'PrepareEnvsInstallDepsHandler', + 'PrepareEnvsReadConfigsHandler', + 'PrepareRunnersDumpConfigsHandler', + 'PrepareRunnersInstallRunnerAndPresetsHandler', 'DumpConfigSaveHandler', ] diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py index 908c5c8..6d59d4d 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py @@ -1,6 +1,5 @@ import dataclasses - -import tomlkit +import pathlib from finecode_extension_api import code_action from finecode_extension_api.actions import dump_config as dump_config_action @@ -22,5 +21,42 @@ async def run( finecode_config = run_context.raw_config_dump.get('tool', {}).get('finecode', {}) if 'presets' in finecode_config: del finecode_config['presets'] + + # apply changes to dependencies from env configuration to deps groups + for env_name, env_config in finecode_config.get('env', {}).items(): + if 'dependencies' not in env_config: + continue + + env_deps_group = run_context.raw_config_dump.get('dependency-groups', {}).get(env_name, []) + dependencies = env_config['dependencies'] + for dep_name, dep_params in dependencies.items(): + # handle 'path'. 'editable' cannot be handled here because dependency + # specifier doesn't support it. It will read and processed by + # `install_deps` action + if 'path' in dep_params: + # replace dependency version / source in dependency group to this path + try: + # check for string because dependency can be also dictionary like '{ "include-group": "runtime"}' + dep_idx_in_group = next(idx for idx, dep in enumerate(env_deps_group) if isinstance(dep, str) and get_dependency_name(dep) == dep_name) + except StopIteration: + continue + + resolved_path_to_dep = pathlib.Path(dep_params['path']) + if not resolved_path_to_dep.is_absolute(): + # resolve relative to project dir where project def file is + resolved_path_to_dep = payload.source_file_path.parent / resolved_path_to_dep + new_dep_str_in_group = f"{dep_name} @ file://{resolved_path_to_dep.as_posix()}" + env_deps_group[dep_idx_in_group] = new_dep_str_in_group + + return dump_config_action.DumpConfigRunResult(config_dump=run_context.raw_config_dump) + + +def get_dependency_name(dependency_str: str) -> str: + # simplified way for now: find the first character which is not allowed in package + # name + for idx, ch in enumerate(dependency_str): + if not ch.isalnum() and ch not in "-_": + return dependency_str[:idx] - return dump_config_action.DumpConfigRunResult() + # dependency can consist also just of package name without version + return dependency_str diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py index cca5eb1..3805958 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py @@ -31,4 +31,4 @@ async def run( file_path=payload.target_file_path, file_content=raw_config_str ) - return dump_config_action.DumpConfigRunResult() + return dump_config_action.DumpConfigRunResult(config_dump=run_context.raw_config_dump) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py index 23afbe5..9347719 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py @@ -1,8 +1,6 @@ import dataclasses import shutil -import tomlkit - from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_envs as prepare_envs_action from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger @@ -25,7 +23,7 @@ async def run( ) -> prepare_envs_action.PrepareEnvsRunResult: project_defs_pathes = set([env_info.project_def_path for env_info in payload.envs]) if len(project_defs_pathes) != 1: - ... # TODO: error + raise code_action.ActionFailedException("prepare_envs action currently supports only preparing environments in the same project where it is running(dump_configs handler)") project_raw_config = await self.project_info_provider.get_project_raw_config() @@ -34,7 +32,7 @@ async def run( # TODO: unify with call of dump_config in CLI dump_dir_path = project_dir_path / 'finecode_config_dump' try: - await self.action_runner.run_action(name='dump_config', payload={ + dump_config_result = await self.action_runner.run_action(name='dump_config', payload={ "source_file_path": project_def_path, "project_raw_config": project_raw_config, "target_file_path": dump_dir_path / 'pyproject.toml' @@ -42,9 +40,10 @@ async def run( new_project_def_path = dump_dir_path / 'pyproject.toml' for env_info in payload.envs: run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = new_project_def_path + run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = dump_config_result['config_dump'] except iactionrunner.BaseRunActionException as exception: - self.logger.exception(exception) # TODO - + raise code_action.ActionFailedException(f"Running 'dump_config' action as part of 'prepare_envs' failed: {type(exception)}, {exception.message}") + # after dumping config in another directory, pathes to project files like # readme and source files are wrong. We cannot just change the pathes to the new # one in project configuration, because they would be outside of the project(in @@ -56,21 +55,21 @@ async def run( # to original source files, not to temporary symlinks # # question: filemanager should be used here? - for item in project_dir_path.iterdir(): - if item.name == 'finecode_config_dump' or item.name == 'pyproject.toml': - # ignore: - # - dir with dumped config - # - dumped config - continue + # for item in project_dir_path.iterdir(): + # if item.name == 'finecode_config_dump' or item.name == 'pyproject.toml': + # # ignore: + # # - dir with dumped config + # # - dumped config + # continue - new_item_path = dump_dir_path / item.name - if new_item_path.exists(): - if new_item_path.is_symlink(): - new_item_path.unlink() - elif new_item_path.is_dir(): - shutil.rmtree(new_item_path) - else: - new_item_path.unlink() - new_item_path.symlink_to(item, target_is_directory=item.is_dir()) + # new_item_path = dump_dir_path / item.name + # if new_item_path.exists(): + # if new_item_path.is_symlink(): + # new_item_path.unlink() + # elif new_item_path.is_dir(): + # shutil.rmtree(new_item_path) + # else: + # new_item_path.unlink() + # new_item_path.symlink_to(item, target_is_directory=item.is_dir()) return prepare_envs_action.PrepareEnvsRunResult(errors=[]) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py new file mode 100644 index 0000000..424c98f --- /dev/null +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py @@ -0,0 +1,76 @@ +import asyncio +import dataclasses +import itertools +import shutil + +from finecode_extension_api import code_action +from finecode_extension_api.actions import prepare_envs as prepare_envs_action +from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger +from .dump_config import get_dependency_name + + +@dataclasses.dataclass +class PrepareEnvsInstallDepsHandlerConfig(code_action.ActionHandlerConfig): ... + + +class PrepareEnvsInstallDepsHandler( + code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, PrepareEnvsInstallDepsHandlerConfig] +): + def __init__(self, action_runner: iactionrunner.IActionRunner, logger: ilogger.ILogger) -> None: + self.action_runner = action_runner + self.logger = logger + + async def run( + self, payload: prepare_envs_action.PrepareEnvsRunPayload, run_context: prepare_envs_action.PrepareEnvsRunContext + ) -> prepare_envs_action.PrepareEnvsRunResult: + envs = payload.envs + + install_deps_tasks: list[asyncio.Task] = [] + try: + async with asyncio.TaskGroup() as tg: + for env in envs: + project_def = run_context.project_def_by_venv_dir_path[env.venv_dir_path] + + # straightforward solution for now + deps_groups = project_def.get('dependency-groups', {}) + env_raw_deps = deps_groups.get(env.name, []) + env_deps_config = project_def.get('tool', {}).get('finecode', {}).get('env', {}).get(env.name, {}).get('dependencies', {}) + dependencies = [] + + process_raw_deps(env_raw_deps, env_deps_config, dependencies, deps_groups) + + task = tg.create_task(self.action_runner.run_action(name='install_deps_in_env', payload={ + "env_name": env.name, + "venv_dir_path": env.venv_dir_path, + "project_dir_path": env.project_def_path.parent, + "dependencies": dependencies + })) + install_deps_tasks.append(task) + except ExceptionGroup as eg: + error_str = '. '.join([str(exception) for exception in eg.exceptions]) + raise code_action.ActionFailedException(error_str) + + install_deps_results = [task.result() for task in install_deps_tasks] + errors: list[str] = list(itertools.chain.from_iterable([result['errors'] for result in install_deps_results])) + + return prepare_envs_action.PrepareEnvsRunResult(errors=errors) + + +def process_raw_deps(raw_deps: list, env_deps_config, dependencies, deps_groups) -> None: + for raw_dep in raw_deps: + if isinstance(raw_dep, str): + name = get_dependency_name(raw_dep) + version_or_source = raw_dep[len(name):] + editable = env_deps_config.get(name, {}).get('editable', False) + dependencies.append({"name": name, "version_or_source": version_or_source, "editable": editable}) + elif isinstance(raw_dep, dict) and 'include-group' in raw_dep: + included_group_deps = deps_groups.get(raw_dep['include-group'], []) + process_raw_deps(included_group_deps, env_deps_config, dependencies, deps_groups) + + +def raw_dep_to_dep_dict(raw_dep: str, env_deps_config: dict) -> dict[str, str | bool]: + name = get_dependency_name(raw_dep) + version_or_source = raw_dep[len(name):] + editable = env_deps_config.get(name, {}).get('editable', False) + dep_dict = {"name": name, "version_or_source": version_or_source, "editable": editable} + return dep_dict diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py new file mode 100644 index 0000000..1b353ff --- /dev/null +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py @@ -0,0 +1,39 @@ +import dataclasses +import shutil + +import tomlkit + +from finecode_extension_api import code_action +from finecode_extension_api.actions import prepare_envs as prepare_envs_action +from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger + + +@dataclasses.dataclass +class PrepareEnvsReadConfigsHandlerConfig(code_action.ActionHandlerConfig): ... + + +class PrepareEnvsReadConfigsHandler( + code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, PrepareEnvsReadConfigsHandlerConfig] +): + def __init__(self, action_runner: iactionrunner.IActionRunner, project_info_provider: iprojectinfoprovider.IProjectInfoProvider, logger: ilogger.ILogger) -> None: + self.action_runner = action_runner + self.project_info_provider = project_info_provider + self.logger = logger + + async def run( + self, payload: prepare_envs_action.PrepareEnvsRunPayload, run_context: prepare_envs_action.PrepareEnvsRunContext + ) -> prepare_envs_action.PrepareEnvsRunResult: + project_defs_pathes = set([env_info.project_def_path for env_info in payload.envs]) + if len(project_defs_pathes) != 1: + ... # TODO: error + + project_raw_config = await self.project_info_provider.get_project_raw_config() + + project_def_path = project_defs_pathes.pop() + project_dir_path = project_def_path.parent + + for env_info in payload.envs: + run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = project_def_path + run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = project_raw_config + + return prepare_envs_action.PrepareEnvsRunResult(errors=[]) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py new file mode 100644 index 0000000..350dbe8 --- /dev/null +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py @@ -0,0 +1,47 @@ +import dataclasses +import shutil + +from finecode_extension_api import code_action +from finecode_extension_api.actions import prepare_runners as prepare_runners_action +from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger + + +@dataclasses.dataclass +class PrepareRunnersDumpConfigsHandlerConfig(code_action.ActionHandlerConfig): ... + + +class PrepareRunnersDumpConfigsHandler( + code_action.ActionHandler[prepare_runners_action.PrepareRunnersAction, PrepareRunnersDumpConfigsHandlerConfig] +): + def __init__(self, action_runner: iactionrunner.IActionRunner, project_info_provider: iprojectinfoprovider.IProjectInfoProvider, logger: ilogger.ILogger) -> None: + self.action_runner = action_runner + self.project_info_provider = project_info_provider + self.logger = logger + + async def run( + self, payload: prepare_runners_action.PrepareRunnersRunPayload, run_context: prepare_runners_action.PrepareRunnersRunContext + ) -> prepare_runners_action.PrepareRunnersRunResult: + project_defs_pathes = set([env_info.project_def_path for env_info in payload.envs]) + if len(project_defs_pathes) != 1: + raise code_action.ActionFailedException("prepare_envs action currently supports only preparing environments in the same project where it is running(dump_configs handler)") + + project_raw_config = await self.project_info_provider.get_project_raw_config() + + project_def_path = project_defs_pathes.pop() + project_dir_path = project_def_path.parent + # TODO: unify with call of dump_config in CLI + dump_dir_path = project_dir_path / 'finecode_config_dump' + try: + dump_config_result = await self.action_runner.run_action(name='dump_config', payload={ + "source_file_path": project_def_path, + "project_raw_config": project_raw_config, + "target_file_path": dump_dir_path / 'pyproject.toml' + }) + new_project_def_path = dump_dir_path / 'pyproject.toml' + for env_info in payload.envs: + run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = new_project_def_path + run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = dump_config_result['config_dump'] + except iactionrunner.BaseRunActionException as exception: + raise code_action.ActionFailedException(f"Running 'dump_config' action as part of 'prepare_envs' failed: {type(exception)}, {exception.message}") + + return prepare_runners_action.PrepareRunnersRunResult(errors=[]) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py new file mode 100644 index 0000000..7a9bc41 --- /dev/null +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py @@ -0,0 +1,90 @@ +import asyncio +import dataclasses +import itertools +import shutil + +from finecode_extension_api import code_action +from finecode_extension_api.actions import prepare_runners as prepare_runners_action +from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger +from .dump_config import get_dependency_name +from .prepare_envs_install_deps import raw_dep_to_dep_dict + + +@dataclasses.dataclass +class PrepareRunnersInstallRunnerAndPresetsHandlerConfig(code_action.ActionHandlerConfig): ... + + +class PrepareRunnersInstallRunnerAndPresetsHandler( + code_action.ActionHandler[prepare_runners_action.PrepareRunnersAction, PrepareRunnersInstallRunnerAndPresetsHandlerConfig] +): + def __init__(self, action_runner: iactionrunner.IActionRunner, logger: ilogger.ILogger) -> None: + self.action_runner = action_runner + self.logger = logger + + async def run( + self, payload: prepare_runners_action.PrepareRunnersRunPayload, run_context: prepare_runners_action.PrepareRunnersRunContext + ) -> prepare_runners_action.PrepareRunnersRunResult: + # find finecode_extension_runner in deps + # find presets in config and their version in deps + # install all these packages + envs = payload.envs + + install_deps_tasks: list[asyncio.Task] = [] + try: + async with asyncio.TaskGroup() as tg: + for env in envs: + project_def = run_context.project_def_by_venv_dir_path[env.venv_dir_path] + + presets_in_config = project_def.get('tool', {}).get('finecode', {}).get('presets', []) + presets_packages_names: list[str] = [] + for preset_def in presets_in_config: + try: + preset_package = preset_def.get('source') + except KeyError: + # workspace manager validates configuration and source should + # always exist, but still handle + raise code_action.ActionFailedException(f"preset has no source: {preset_def} in {run_context.project_def_path_by_venv_dir_path[env.venv_dir_path]}") + presets_packages_names.append(preset_package) + + # straightforward solution for now + deps_groups = project_def.get('dependency-groups', {}) + env_raw_deps = deps_groups.get(env.name, []) + env_deps_config = project_def.get('tool', {}).get('finecode', {}).get('env', {}).get(env.name, {}).get('dependencies', {}) + dependencies = [] + + try: + runner_dep = next(dep for dep in env_raw_deps if isinstance(dep, str) and get_dependency_name(dep) == 'finecode_extension_runner') + except StopIteration: + raise code_action.ActionFailedException(f"prepare_runners expects finecode_extension_runner dependency in each environment, but it was not found in {env.name} (install_runner_and_presets handler)") + + runner_dep_dict = raw_dep_to_dep_dict(raw_dep=runner_dep, env_deps_config=env_deps_config) + dependencies.append(runner_dep_dict) + + for preset_package in presets_packages_names: + try: + preset_dep = next(dep for dep in env_raw_deps if isinstance(dep, str) and get_dependency_name(dep) == preset_package) + except StopIteration: + if env.name == 'dev_no_runtime': + # all preset packages must be in 'dev_no_runtime' env + raise code_action.ActionFailedException(f"'{preset_package}' is used as preset source, but not declared in 'dev_no_runtime' dependency group") + else: + continue + + preset_dep_dict = raw_dep_to_dep_dict(raw_dep=preset_dep, env_deps_config=env_deps_config) + dependencies.append(preset_dep_dict) + + task = tg.create_task(self.action_runner.run_action(name='install_deps_in_env', payload={ + "env_name": env.name, + "venv_dir_path": env.venv_dir_path, + "project_dir_path": env.project_def_path.parent, + "dependencies": dependencies + })) + install_deps_tasks.append(task) + except ExceptionGroup as eg: + error_str = '. '.join([str(exception) for exception in eg.exceptions]) + raise code_action.ActionFailedException(error_str) + + install_deps_results = [task.result() for task in install_deps_tasks] + errors: list[str] = list(itertools.chain.from_iterable([result['errors'] for result in install_deps_results])) + + return prepare_runners_action.PrepareRunnersRunResult(errors=errors) diff --git a/finecode_extension_runner/src/finecode_extension_runner/cli.py b/finecode_extension_runner/src/finecode_extension_runner/cli.py index 1cef1ef..43af2f1 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/cli.py +++ b/finecode_extension_runner/src/finecode_extension_runner/cli.py @@ -1,4 +1,5 @@ import os +import sys from pathlib import Path from importlib import metadata @@ -25,8 +26,8 @@ def main(): type=click.Path(exists=True, file_okay=False, resolve_path=True, path_type=Path), required=True, ) -@click.option("--env-name", "env_name", type=str, default="unknown") -def start(trace: bool, debug: bool, debug_port: int, project_path: Path, env_name: str): +@click.option("--env-name", "env_name", type=str) +def start(trace: bool, debug: bool, debug_port: int, project_path: Path, env_name: str | None): if debug is True: import debugpy @@ -38,6 +39,10 @@ def start(trace: bool, debug: bool, debug_port: int, project_path: Path, env_nam except Exception as e: logger.info(e) + if env_name is None: + click.echo("Environment name(--env-name) is required", err=True) + sys.exit(1) + global_state.log_level = "INFO" if trace is False else "TRACE" global_state.project_dir_path = project_path # asyncio.run(runner_start.start_runner()) diff --git a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py index f6527ef..6e511a9 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py +++ b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py @@ -67,8 +67,9 @@ async def run_action_wrapper(action_name: str, payload: dict[str, Any]) -> dict[ options = schemas.RunActionOptions(result_format='json') response = await run_action.run_action(request=request, options=options) if response.return_code != 0: + loguru_logger.get_logger().error(response.result) # TODO: pass details - raise iactionrunner.ActionRunFailed() + raise iactionrunner.ActionRunFailed(str(response.return_code)) return response.result diff --git a/finecode_extension_runner/src/finecode_extension_runner/impls/process_executor.py b/finecode_extension_runner/src/finecode_extension_runner/impls/process_executor.py index aad6aff..1b7434e 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/impls/process_executor.py +++ b/finecode_extension_runner/src/finecode_extension_runner/impls/process_executor.py @@ -26,7 +26,6 @@ def activate(self) -> collections.abc.Iterator[None]: try: yield except Exception as exc: - logger.exception(exc) raise exc finally: if self._py_process_executor is not None: diff --git a/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py index 4c95a7b..4607b3b 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py +++ b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py @@ -202,12 +202,22 @@ async def run_action(ls: lsp_server.LanguageServer, params): logger.trace(f"Run action: {params[0]}") request = schemas.RunActionRequest(action_name=params[0], params=params[1]) options = schemas.RunActionOptions(**params[2] if params[2] is not None else {}) - # pygls sends uncatched exceptions(e.g. internal errors) to client. Log them as well + try: response = await services.run_action(request=request, options=options) - except Exception as e: - logger.exception(f"Run action error: {e}") - raise e + except Exception as exception: + error_msg = "" + if isinstance(exception, services.ActionFailedException): + logger.error(f"Run action failed: {exception.message}") + error_msg = exception.message + else: + logger.error("Unhandled exception in action run:") + logger.exception(exception) + error_msg = f'{type(exception)}: {str(exception)}' + return { + "error": error_msg + } + # dict key can be path, but pygls fails to handle slashes in dict keys, use strings # representation of result instead until the problem is properly solved result_str = json.dumps(response.to_dict()["result"]) @@ -226,5 +236,7 @@ async def reload_action(ls: lsp_server.LanguageServer, params): async def resolve_package_path(ls: lsp_server.LanguageServer, params): logger.trace(f"Resolve package path: {params}") + # TODO: handle properly ValueError result = services.resolve_package_path(params[0]) + logger.trace(f"Resolved {params[0]} to {result}") return {"packagePath": result} diff --git a/finecode_extension_runner/src/finecode_extension_runner/services.py b/finecode_extension_runner/src/finecode_extension_runner/services.py index 1e9fe5d..4ea1acd 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/services.py +++ b/finecode_extension_runner/src/finecode_extension_runner/services.py @@ -15,7 +15,7 @@ from finecode_extension_runner import project_dirs, run_utils, schemas from finecode_extension_api import code_action, textstyler from finecode_extension_runner._services import run_action as run_action_module -from finecode_extension_runner._services.run_action import run_action +from finecode_extension_runner._services.run_action import run_action, ActionFailedException from finecode_extension_runner.di import bootstrap as di_bootstrap diff --git a/presets/fine_python_format/pyproject.toml b/presets/fine_python_format/pyproject.toml index b74b89f..025ec95 100644 --- a/presets/fine_python_format/pyproject.toml +++ b/presets/fine_python_format/pyproject.toml @@ -5,12 +5,16 @@ description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["fine_extension_api==0.1.*"] +dependencies = ["finecode_extension_api==0.1.*"] -[tool.poetry.group.dev.dependencies] -finecode = { version = "0.2.0" } -finecode_dev_common_preset = { path = "../../finecode_dev_common_preset", develop = true } +# TODO: rework +# [tool.poetry.group.dev.dependencies] +# finecode = { version = "0.2.0" } +# finecode_dev_common_preset = { path = "../../finecode_dev_common_preset", develop = true } [build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +requires = ["setuptools>=64"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.package-data] +fine_python_format = ["preset.toml"] diff --git a/presets/fine_python_format/setup.py b/presets/fine_python_format/setup.py new file mode 100644 index 0000000..2196206 --- /dev/null +++ b/presets/fine_python_format/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="fine_python_format", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/presets/fine_python_lint/setup.py b/presets/fine_python_lint/setup.py new file mode 100644 index 0000000..bfad464 --- /dev/null +++ b/presets/fine_python_lint/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="fine_python_lint", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/presets/fine_python_recommended/setup.py b/presets/fine_python_recommended/setup.py new file mode 100644 index 0000000..128ad4f --- /dev/null +++ b/presets/fine_python_recommended/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="fine_python_recommended", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..acf765f --- /dev/null +++ b/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="finecode", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/src/finecode/base_config.toml b/src/finecode/base_config.toml new file mode 100644 index 0000000..a35d0bc --- /dev/null +++ b/src/finecode/base_config.toml @@ -0,0 +1,60 @@ +[tool.finecode.action.prepare_envs] +source = "finecode_extension_api.actions.prepare_envs.PrepareEnvsAction" +handlers = [ + { name = "prepare_envs_dump_configs", source = "finecode_extension_runner.action_handlers.PrepareEnvsDumpConfigsHandler", env = "dev_workspace", dependencies = [ + ] }, + { name = "prepare_envs_install_deps", source = "finecode_extension_runner.action_handlers.PrepareEnvsInstallDepsHandler", env = "dev_workspace", dependencies = [ + ] }, +] +config = {} + +# preparing dev workspaces doesn't need dumping config for two reasons: +# - dependencies in `dev_workspace` are expected to be simple and installable +# without dump +# - dumping is modifiable as action, so it can be correctly done only in +# dev_workspace env of the project and we just create it here, it doesn't +# exist yet +[tool.finecode.action.prepare_dev_workspaces_envs] +source = "finecode_extension_api.actions.prepare_envs.PrepareEnvsAction" +handlers = [ + { name = "prepare_venvs", source = "fine_python_virtualenv.VirtualenvPrepareEnvHandler", env = "dev_workspace", dependencies = [ + "fine_python_virtualenv @ file:///home/user/Development/FineCode/finecode/extensions/fine_python_virtualenv", + ] }, + { name = "prepare_envs_read_configs", source = "finecode_extension_runner.action_handlers.PrepareEnvsReadConfigsHandler", env = "dev_workspace", dependencies = [ + ] }, + { name = "prepare_envs_install_deps", source = "finecode_extension_runner.action_handlers.PrepareEnvsInstallDepsHandler", env = "dev_workspace", dependencies = [ + ] }, +] +config = {} + +[tool.finecode.action.prepare_runners] +source = "finecode_extension_api.actions.prepare_runners.PrepareRunnersAction" +handlers = [ + { name = "prepare_runners_venvs", source = "fine_python_virtualenv.VirtualenvPrepareRunnersHandler", env = "dev_workspace", dependencies = [ + "@ file:///home/user/Development/FineCode/finecode/extensions/fine_python_virtualenv", + ] }, + { name = "prepare_runners_dump_configs", source = "finecode_extension_runner.action_handlers.PrepareRunnersDumpConfigsHandler", env = "dev_workspace", dependencies = [ + ] }, + { name = "prepare_runners_install_runner_and_presets", source = "finecode_extension_runner.action_handlers.PrepareEnvsInstallDepsHandler", env = "dev_workspace", dependencies = [ + ] }, +] +config = {} + +[tool.finecode.action.dump_config] +source = "finecode_extension_api.actions.dump_config.DumpConfigAction" +handlers = [ + { name = "dump_config", source = "finecode_extension_runner.action_handlers.DumpConfigHandler", env = "dev_workspace", dependencies = [ + ] }, + { name = "dump_config_save", source = "finecode_extension_runner.action_handlers.DumpConfigSaveHandler", env = "dev_workspace", dependencies = [ + ] }, +] +config = {} + +[tool.finecode.action.install_deps_in_env] +source = "finecode_extension_api.actions.install_deps_in_env.InstallDepsInEnvAction" +handlers = [ + { name = "install_deps_with_pip", source = "fine_python_pip.PipInstallDepsInEnvHandler", env = "dev_workspace", dependencies = [ + "fine_python_pip @ file:///home/user/Development/FineCode/finecode/extensions/fine_python_pip", + ] }, +] +config = {} diff --git a/src/finecode/cli.py b/src/finecode/cli.py index e6e7791..db1c2b2 100644 --- a/src/finecode/cli.py +++ b/src/finecode/cli.py @@ -196,11 +196,14 @@ def prepare_envs(trace: bool, logger_utils.init_logger(trace=trace, stdout=True) try: - asyncio.run(prepare_envs_cmd.prepare_envs(workdir_path=pathlib.Path(os.getcwd()))) - except Exception as exception: # TODO - logger.exception(exception) - click.echo(exception, err=True) + asyncio.run(prepare_envs_cmd.prepare_envs(workdir_path=pathlib.Path(os.getcwd()), recreate=recreate)) + except prepare_envs_cmd.PrepareEnvsFailed as exception: + click.echo(exception.args[0], err=True) sys.exit(1) + except Exception as exception: + logger.exception(exception) + click.echo("Unexpected error, see logs in file for more details", err=True) + sys.exit(2) @cli.command() diff --git a/src/finecode/cli_app/prepare_envs.py b/src/finecode/cli_app/prepare_envs.py index 48644a9..89d577e 100644 --- a/src/finecode/cli_app/prepare_envs.py +++ b/src/finecode/cli_app/prepare_envs.py @@ -3,11 +3,15 @@ from loguru import logger from finecode import context, services, domain -from finecode.config import read_configs, collect_actions +from finecode.config import read_configs, collect_actions, config_models from finecode.cli_app import run as run_cli from finecode.runner import manager as runner_manager +class PrepareEnvsFailed(Exception): + ... + + async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: # similar to `run_actions`, but with certain differences: # - prepare_envs doesn't support presets because `dev_no_runtime` env most @@ -21,44 +25,97 @@ async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: # `prepare_envs` can be run only from workspace/project root. Validate this if workdir_path not in ws_context.ws_projects: - # TODO: better exception - raise Exception("prepare_env can be run only from workspace/project root") + raise PrepareEnvsFailed("prepare_env can be run only from workspace/project root") invalid_projects = [project for project in ws_context.ws_projects.values() if project.status == domain.ProjectStatus.CONFIG_INVALID] if len(invalid_projects) > 0: - raise Exception(f"Projects have invalid configuration: {invalid_projects}") + raise PrepareEnvsFailed(f"Projects have invalid configuration: {invalid_projects}") # prepare envs only in projects with valid configurations and which use finecode projects = [project for project in ws_context.ws_projects.values() if project.status == domain.ProjectStatus.CONFIG_VALID] # Collect actions in relevant projects for project in projects: - await read_configs.read_project_config(project=project, ws_context=ws_context, resolve_presets=False) - collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) - - actions_by_projects: dict[pathlib.Path, list[str]] = {project.dir_path: ['prepare_envs'] for project in projects} - # action payload can be kept empty because it will be filled in payload preprocessor - action_payload: dict[str, str | bool] = {"recreate": recreate} + try: + await read_configs.read_project_config(project=project, ws_context=ws_context, resolve_presets=False) + collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + except config_models.ConfigurationError as exception: + raise PrepareEnvsFailed(f"Reading project config and collecting actions in {project.dir_path} failed: {exception.message}") try: # try to start runner in 'dev_workspace' env of each project. If venv doesn't # exist or doesn't work, recreate it by running actions in the current env. + if recreate: + remove_dev_workspace_envs(projects=projects, workdir_path=workdir_path) + await check_or_recreate_all_dev_workspace_envs(projects=projects, workdir_path=workdir_path, ws_context=ws_context) - # now all 'dev_workspace' envs are valid, run 'prepare_envs' in them to create - # envs in each subproject. + # now all 'dev_workspace' envs are valid, run 'prepare_runners' in them to create + # venvs and install runners and presets in them. This is required to be able to + # resolve presets which can contain additional dependency configurations. + # Only then run `prepare_envs` to install dependencies in each subproject. + + actions_by_projects: dict[pathlib.Path, list[str]] = {project.dir_path: ['prepare_runners'] for project in projects} + # action payload can be kept empty because it will be filled in payload preprocessor + action_payload: dict[str, str | bool] = {"recreate": recreate} + await run_cli.start_required_environments(actions_by_projects, ws_context) try: - await run_cli.run_actions_in_all_projects( + (result_output, result_return_code) = await run_cli.run_actions_in_all_projects( actions_by_projects, action_payload, ws_context, concurrently=True ) except run_cli.RunFailed as error: logger.error(error.message) + result_output = error.message + result_return_code = 1 + + if result_return_code != 0: + raise PrepareEnvsFailed(result_output) + + # reread projects configs, now with resolved presets + # to be able to resolve presets, start dev_no_runtime runners first + try: + await runner_manager.start_runners_with_presets(projects=projects, ws_context=ws_context) + except runner_manager.RunnerFailedToStart as exception: + raise PrepareEnvsFailed(f"Starting runners with presets failed: {exception.message}") + + for project in projects: + try: + await read_configs.read_project_config(project=project, ws_context=ws_context) + collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + except config_models.ConfigurationError as exception: + raise PrepareEnvsFailed(f"Rereading project config with presets and collecting actions in {project.dir_path} failed: {exception.message}") + + actions_by_projects: dict[pathlib.Path, list[str]] = {project.dir_path: ['prepare_envs'] for project in projects} + # action payload can be kept empty because it will be filled in payload preprocessor + action_payload: dict[str, str | bool] = {"recreate": recreate} + + try: + (result_output, result_return_code) = await run_cli.run_actions_in_all_projects( + actions_by_projects, action_payload, ws_context, concurrently=True + ) + except run_cli.RunFailed as error: + logger.error(error.message) + result_output = error.message + result_return_code = 1 + + if result_return_code != 0: + raise PrepareEnvsFailed(result_output) finally: services.on_shutdown(ws_context) +def remove_dev_workspace_envs(projects: list[domain.Project], workdir_path: pathlib.Path) -> None: + for project in projects: + if project.dir_path == workdir_path: + # skip removing `dev_workspace` env of the current project, because user + # is responsible for keeping it correct + continue + + runner_manager.remove_runner_venv(runner_dir=project.dir_path, env_name='dev_workspace') + + async def check_or_recreate_all_dev_workspace_envs(projects: list[domain.Project], workdir_path: pathlib.Path, ws_context: context.WorkspaceContext) -> None: # NOTE: this function can start new extensions runner, don't forget to call # on_shutdown if you use it @@ -122,11 +179,14 @@ async def check_or_recreate_all_dev_workspace_envs(projects: list[domain.Project envs += invalid_envs # TODO: check result - await services.run_action( - action_name='prepare_dev_workspaces_envs', - params={ "envs": envs, }, - project_def=current_project, - ws_context=ws_context, - result_format=services.RunResultFormat.STRING, - preprocess_payload=False - ) + try: + await services.run_action( + action_name='prepare_dev_workspaces_envs', + params={ "envs": envs, }, + project_def=current_project, + ws_context=ws_context, + result_format=services.RunResultFormat.STRING, + preprocess_payload=False + ) + except services.ActionRunFailed as exception: + raise PrepareEnvsFailed(f"'prepare_dev_workspaces_env' failed in {current_project.name}: {exception.message}") diff --git a/src/finecode/cli_app/run.py b/src/finecode/cli_app/run.py index 1153930..8b26e33 100644 --- a/src/finecode/cli_app/run.py +++ b/src/finecode/cli_app/run.py @@ -7,7 +7,7 @@ from loguru import logger from finecode import context, domain, services -from finecode.config import read_configs, collect_actions +from finecode.config import read_configs, collect_actions, config_models from finecode.runner import manager as runner_manager, runner_info @@ -78,6 +78,45 @@ async def run_actions( for project_dir_path, project in ws_context.ws_projects.items() if project.name in projects_names } + + # make sure all projects use finecode + config_problem_found = False + for project in ws_context.ws_projects.values(): + if project.status != domain.ProjectStatus.CONFIG_VALID: + if project.status == domain.ProjectStatus.NO_FINECODE: + logger.error(f"You asked to run action in project '{project.name}', but finecode is not used in it(=there is no 'dev_workspace' environment with 'finecode' package in it)") + config_problem_found = True + elif project.status == domain.ProjectStatus.CONFIG_INVALID: + logger.error(f"You asked to run action in project '{project.name}', but its configuration is invalid(see logs above for more details)") + config_problem_found = True + else: + logger.error(f"You asked to run action in project '{project.name}', but it has unexpected status: {project.status}") + config_problem_found = True + + if config_problem_found: + raise RunFailed("There is a problem with configuration. See previous messages for more details") + else: + # filter out packages that don't use finecode + ws_context.ws_projects = { + project_dir_path: project + for project_dir_path, project in ws_context.ws_projects.items() + if project.status != domain.ProjectStatus.NO_FINECODE + } + + # check that configuration of packages that use finecode is valid + config_problem_found = False + for project in ws_context.ws_projects.values(): + if project.status == domain.ProjectStatus.CONFIG_VALID: + continue + elif project.status == domain.ProjectStatus.CONFIG_INVALID: + logger.error(f"Project '{project.name}' has invalid config, see messages above for more details") + config_problem_found = True + else: + logger.error(f"Project '{project.name}' has unexpected status: {project.status}") + config_problem_found = True + + if config_problem_found: + raise RunFailed("There is a problem with configuration. See previous messages for more details") projects: list[domain.Project] = [] if projects_names is not None: @@ -97,11 +136,17 @@ async def run_actions( f"One or more projects are misconfigured, runners for them didn't" f" start: {exception.message}. Check logs for details." ) + except Exception as exception: + logger.error("Unexpected exception:") + logger.exception(exception) # 2. Collect actions in relevant projects for project in projects: - await read_configs.read_project_config(project=project, ws_context=ws_context) - collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + try: + await read_configs.read_project_config(project=project, ws_context=ws_context) + collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + except config_models.ConfigurationError as exception: + raise RunFailed(f"Found configuration problem: {exception.message}") actions_by_projects: dict[pathlib.Path, list[str]] = {} if projects_names is not None: @@ -275,7 +320,7 @@ async def run_actions_in_running_project( except ExceptionGroup as eg: for exception in eg.exceptions: if isinstance(exception, services.ActionRunFailed): - logger.error(exception.message) + logger.error(f'{exception.message} in {project.name}') else: logger.error("Unexpected exception:") logger.exception(exception) @@ -299,6 +344,8 @@ async def run_actions_in_running_project( result_format=services.RunResultFormat.STRING, ) except Exception as error: + # TODO: which are expected here? + logger.error("Unexpected exception") logger.exception(error) raise RunFailed(f"Running of action {action_name} failed") diff --git a/src/finecode/config/collect_actions.py b/src/finecode/config/collect_actions.py index 6c2196a..acc14ad 100644 --- a/src/finecode/config/collect_actions.py +++ b/src/finecode/config/collect_actions.py @@ -40,8 +40,12 @@ def _collect_actions_in_config( for action_name, action_def_raw in ( config["tool"]["finecode"].get("action", {}).items() ): - # TODO: handle validation errors - action_def = config_models.ActionDefinition(**action_def_raw) + + try: + action_def = config_models.ActionDefinition(**action_def_raw) + except config_models.ValidationError as exception: + raise config_models.ConfigurationError(str(exception)) + new_action = domain.Action( name=action_name, handlers=[ diff --git a/src/finecode/config/config_models.py b/src/finecode/config/config_models.py index 7cb9597..f90cee4 100644 --- a/src/finecode/config/config_models.py +++ b/src/finecode/config/config_models.py @@ -1,6 +1,6 @@ from typing import Any -from pydantic import BaseModel +from pydantic import BaseModel, ValidationError class FinecodePresetDefinition(BaseModel): @@ -45,3 +45,8 @@ class ActionDefinition(BaseModel): class ViewDefinition(BaseModel): name: str source: str + + +class ConfigurationError(Exception): + def __init__(self, message: str) -> None: + self.message = message diff --git a/src/finecode/config/read_configs.py b/src/finecode/config/read_configs.py index d9c7aad..85e312a 100644 --- a/src/finecode/config/read_configs.py +++ b/src/finecode/config/read_configs.py @@ -38,7 +38,10 @@ async def read_projects_in_dir( with open(def_file, "rb") as pyproject_file: project_def = toml_loads(pyproject_file.read()).value - if project_def.get("tool", {}).get("finecode", None) is None: + dependency_groups = project_def.get('dependency-groups', {}) + dev_workspace_group = dependency_groups.get('dev_workspace', []) + finecode_in_dev_workspace = any(dep for dep in dev_workspace_group if get_dependency_name(dep) == 'finecode') + if not finecode_in_dev_workspace: status = domain.ProjectStatus.NO_FINECODE actions = [] @@ -54,6 +57,17 @@ async def read_projects_in_dir( return new_projects +def get_dependency_name(dependency_str: str) -> str: + # simplified way for now: find the first character which is not allowed in package + # name + for idx, ch in enumerate(dependency_str): + if not ch.isalnum() and ch not in "-_": + return dependency_str[:idx] + + # dependency can consist also just of package name without version + return dependency_str + + async def read_project_config( project: domain.Project, ws_context: context.WorkspaceContext, resolve_presets: bool = True ) -> None: @@ -64,6 +78,13 @@ async def read_project_config( # TODO: handle error if toml is invalid project_def = toml_loads(pyproject_file.read()).value # TODO: validate that finecode is installed? + + base_config_path = Path(__file__).parent.parent / 'base_config.toml' + # TODO: cache instead of reading each time + with open(base_config_path, 'r') as base_config_file: + base_config = toml_loads(base_config_file.read()).value + _merge_projects_configs(base_config, base_config_path, project_def, project.def_path) + project_def = base_config finecode_raw_config = project_def.get("tool", {}).get("finecode", None) if finecode_raw_config and resolve_presets: @@ -77,49 +98,9 @@ async def read_project_config( def_path=project.def_path, runner=dev_no_runtime_runner, ) - _merge_projects_configs(project_def, new_config) - - # add builtins if they are not overwritten - prepare_envs_action = domain.Action( - name='prepare_envs', - source='finecode_extension_api.actions.prepare_envs.PrepareEnvsAction', - handlers=[ - domain.ActionHandler(name='prepare_envs_venvs', source='fine_python_virtualenv.VirtualenvPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_virtualenv==0.1.*']), - domain.ActionHandler(name='prepare_envs_dump_configs', source='finecode_extension_runner.action_handlers.PrepareEnvsDumpConfigsHandler', config={}, env='dev_workspace', dependencies=[]), - domain.ActionHandler(name='prepare_envs_pip', source='fine_python_pip.PipPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_pip==0.1.*']) - ], - config={} - ) - add_action_to_config_if_new(project_def, prepare_envs_action) - - # preparing dev workspaces doesn't need dumping config for two reasons: - # - depedencies in `dev_workspace` are expected to be simple and installable - # without dump - # - dumping is modifiable as action, so it can be correctly done only in - # dev_workspace env of the project and we just create it here, it doesn't - # exist yet - prepare_dev_workspaces_envs_action = domain.Action( - name='prepare_dev_workspaces_envs', - source='finecode_extension_api.actions.prepare_envs.PrepareEnvsAction', - handlers=[ - domain.ActionHandler(name='prepare_venvs', source='fine_python_virtualenv.VirtualenvPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_virtualenv==0.1.*']), - domain.ActionHandler(name='prepare_venvs_pip', source='fine_python_pip.PipPrepareEnvHandler', config={}, env='dev_workspace', dependencies=['fine_python_pip==0.1.*']) - ], - config={} - ) - add_action_to_config_if_new(project_def, prepare_dev_workspaces_envs_action) - - dump_config_action = domain.Action( - name='dump_config', - source='finecode_extension_api.actions.dump_config.DumpConfigAction', - handlers=[ - domain.ActionHandler(name='dump_config', source='finecode_extension_runner.action_handlers.DumpConfigHandler', config={}, env='dev_workspace', dependencies=[]), - domain.ActionHandler(name='dump_config_save', source='finecode_extension_runner.action_handlers.DumpConfigSaveHandler', config={}, env='dev_workspace', dependencies=[]) - ], - config={} - ) - add_action_to_config_if_new(project_def, dump_config_action) - + if new_config is not None: + _merge_projects_configs(project_def, project.def_path, new_config, project.def_path) + # add runtime dependency group if it's not explicitly declared add_runtime_dependency_group_if_new(project_def) @@ -162,12 +143,11 @@ async def get_preset_project_path( def read_preset_config( config_path: Path, preset_id: str -) -> tuple[dict[str, Any] | None, config_models.PresetDefinition | None]: +) -> tuple[dict[str, Any], config_models.PresetDefinition]: # preset_id is used only for logs to make them more useful logger.trace(f"Read preset config: {preset_id}") if not config_path.exists(): - logger.error(f"preset.toml not found in project '{preset_id}'") - return (None, None) + raise config_models.ConfigurationError(f"preset.toml not found in project '{preset_id}'") with open(config_path, "rb") as preset_toml_file: preset_toml = toml_loads(preset_toml_file.read()).value @@ -185,8 +165,8 @@ def read_preset_config( async def collect_config_from_py_presets( presets_sources: list[str], def_path: Path, runner: runner_info.ExtensionRunnerInfo -) -> dict[str, Any]: - config: dict[str, Any] = {} +) -> dict[str, Any] | None: + config: dict[str, Any] | None = None processed_presets: set[str] = set() presets_to_process: set[PresetToProcess] = set( [ @@ -203,14 +183,14 @@ async def collect_config_from_py_presets( ) if preset_project_path is None: logger.trace(f"Path of preset {preset.source} not found") - continue + raise config_models.ConfigurationError(f"Path of preset {preset.source} in project {def_path.parent} not found") preset_toml_path = preset_project_path / "preset.toml" preset_toml, preset_config = read_preset_config(preset_toml_path, preset.source) - if preset_toml is None or preset_config is None: - continue - - _merge_preset_configs(config, preset_toml) + if config is None: + config = preset_toml + else: + _merge_projects_configs(config, def_path, preset_toml, preset_project_path) new_presets_sources = ( set([extend.source for extend in preset_config.extends]) - processed_presets ) @@ -225,7 +205,7 @@ async def collect_config_from_py_presets( return config -def _merge_projects_configs(config1: dict[str, Any], config2: dict[str, Any]) -> None: +def _merge_projects_configs(config1: dict[str, Any], config1_filepath: Path, config2: dict[str, Any], config2_filepath: Path) -> None: # merge config2 in config1 without overwriting if "tool" not in config1: config1["tool"] = {} @@ -255,94 +235,43 @@ def _merge_projects_configs(config1: dict[str, Any], config2: dict[str, Any]) -> "config" ] action_config.update(action_info["config"]) + elif key == "env": + if 'env' not in tool_finecode_config1: + tool_finecode_config1['env'] = {} + + all_envs_config1 = tool_finecode_config1['env'] + + for env_name, env_config2 in value.items(): + if env_name not in all_envs_config1: + all_envs_config1[env_name] = env_config2 + else: + # merge env configs + env_config1 = all_envs_config1[env_name] + if 'dependencies' in env_config2: + if 'dependencies' not in env_config1: + env_config1['dependencies'] = env_config2['dependencies'] + else: + # merge dependencies + env_config1_deps = env_config1['dependencies'] + for dependency_name, dependency in env_config2['dependencies'].items(): + if dependency_name not in env_config1_deps: + env_config1_deps[dependency_name] = dependency + else: + if 'path' in dependency: + new_path = dependency['path'] + if new_path.startswith('.'): + abs_path = config2_filepath.parent / new_path + new_rel_path = abs_path.relative_to(config1_filepath.parent) + new_path = new_rel_path.as_posix() + env_config1_deps[dependency_name]['path'] = new_path + if 'editable' in dependency: + env_config1_deps[dependency_name]['editable'] = dependency['editable'] elif key in config1: tool_finecode_config1[key].update(value) else: tool_finecode_config1[key] = value -def _merge_preset_configs(config1: dict[str, Any], config2: dict[str, Any]) -> None: - # merge config2 in config1 (in-place) - # config1 is not overwritten by config2 - new_views = config2.get("tool", {}).get("finecode", {}).get("views", None) - new_actions_defs_and_configs = ( - config2.get("tool", {}).get("finecode", {}).get("action", None) - ) - new_actions_handlers_configs = ( - config2.get("tool", {}).get("finecode", {}).get("action_handler", None) - ) - if ( - new_views is not None - or new_actions_defs_and_configs is not None - or new_actions_handlers_configs is not None - ): - if "tool" not in config1: - config1["tool"] = {} - if "finecode" not in config1["tool"]: - config1["tool"]["finecode"] = {} - - if new_views is not None: - if "views" not in config1["tool"]["finecode"]: - config1["tool"]["finecode"]["views"] = [] - config1["tool"]["finecode"]["views"].extend(new_views) - del config2["tool"]["finecode"]["views"] - - if new_actions_defs_and_configs is not None: - if "action" not in config1["tool"]["finecode"]: - config1["tool"]["finecode"]["action"] = {} - - for handler_name, handler_info in new_actions_defs_and_configs.items(): - if handler_name not in config1["tool"]["finecode"]["action"]: - config1["tool"]["finecode"]["action"][handler_name] = {} - - action_def = { - key: value for key, value in handler_info.items() if key != "config" - } - config1["tool"]["finecode"]["action"][handler_name].update(action_def) - - try: - handler_config = handler_info["config"] - except KeyError: - continue - - handler_config.update( - config1["tool"]["finecode"]["action"][handler_name].get( - "config", {} - ) - ) - config1["tool"]["finecode"]["action"][handler_name][ - "config" - ] = handler_config - - del config2["tool"]["finecode"]["action"] - - if new_actions_handlers_configs is not None: - if "action_handler" not in config1["tool"]["finecode"]: - config1["tool"]["finecode"]["action_handler"] = {} - - for handler_name, handler_info in new_actions_handlers_configs.items(): - if handler_name not in config1["tool"]["finecode"]["action_handler"]: - config1["tool"]["finecode"]["action_handler"][handler_name] = {} - - try: - handler_config = handler_info["config"] - except KeyError: - continue - - handler_config.update( - config1["tool"]["finecode"]["action_handler"][handler_name].get( - "config", {} - ) - ) - config1["tool"]["finecode"]["action_handler"][handler_name][ - "config" - ] = handler_config - - del config2["tool"]["finecode"]["action_handler"] - - del config2["tool"]["finecode"] - - def add_action_to_config_if_new(raw_config: dict[str, Any], action: domain.Action) -> None: # adds action to raw config if it is not defined yet. Existing action will be not # overwritten diff --git a/src/finecode/finecode_cmd.py b/src/finecode/finecode_cmd.py index 92ff65f..8939437 100644 --- a/src/finecode/finecode_cmd.py +++ b/src/finecode/finecode_cmd.py @@ -1,8 +1,14 @@ from pathlib import Path +def get_venv_dir_path(project_path: Path, env_name: str) -> Path: + venv_dir_path = project_path / ".venvs" / env_name + return venv_dir_path + + def get_python_cmd(project_path: Path, env_name: str) -> str: - venv_python_path = project_path / ".venvs" / env_name / "bin" / "python" + venv_dir_path = get_venv_dir_path(project_path=project_path, env_name=env_name) + venv_python_path = venv_dir_path / "bin" / "python" if not venv_python_path.exists(): raise ValueError(f"{env_name} venv not found in project {project_path}") diff --git a/src/finecode/payload_preprocessor.py b/src/finecode/payload_preprocessor.py index 28899e8..a0ce625 100644 --- a/src/finecode/payload_preprocessor.py +++ b/src/finecode/payload_preprocessor.py @@ -18,7 +18,7 @@ def preprocess_for_project( if action_name == "format" and "save" not in processed_payload: processed_payload["save"] = True - elif action_name == "prepare_envs": + elif action_name == "prepare_envs" or action_name == "prepare_runners": runtime_venv_path = project_dir_path / '.venvs' / 'runtime' project_def_path = project_dir_path / 'pyproject.toml' envs = [{"name": "runtime", "venv_dir_path": runtime_venv_path, "project_def_path": project_def_path}] diff --git a/src/finecode/runner/manager.py b/src/finecode/runner/manager.py index 202e0ed..6ab1e71 100644 --- a/src/finecode/runner/manager.py +++ b/src/finecode/runner/manager.py @@ -1,6 +1,7 @@ import asyncio import json import os +import shutil from pathlib import Path from typing import Callable, Coroutine @@ -10,7 +11,7 @@ from finecode import dirs_utils from finecode.pygls_client_utils import create_lsp_client_io from finecode import context, domain, finecode_cmd -from finecode.config import collect_actions, read_configs +from finecode.config import collect_actions, read_configs, config_models from finecode.runner import runner_client, runner_info from finecode.utils import iterable_subscribe @@ -79,6 +80,7 @@ async def start_extension_runner( await notify_project_changed(ws_context.ws_projects[runner_dir]) except KeyError: ... + logger.error(f"Project {runner_dir} uses finecode, but env (venv) doesn't exist yet. Run `prepare_env` command to create it") return None process_args: list[str] = [ @@ -236,6 +238,7 @@ async def update_runners(ws_context: context.WorkspaceContext) -> None: if isinstance(exception, runner_client.BaseRunnerRequestException) or isinstance(exception, RunnerFailedToStart): logger.error(exception.message) else: + logger.error("Unexpected exception:") logger.exception(exception) raise RunnerFailedToStart("Failed to start runner") @@ -259,6 +262,7 @@ async def update_runners(ws_context: context.WorkspaceContext) -> None: if isinstance(exception, runner_client.BaseRunnerRequestException): logger.error(exception.message) else: + logger.error("Unexpected exception:") logger.exception(exception) raise RunnerFailedToStart("Failed to initialize runner") @@ -280,18 +284,19 @@ async def start_runners_with_presets(projects: list[domain.Project], ws_context: save_runners_from_tasks_in_context(tasks=new_runners_tasks, ws_context=ws_context) except ExceptionGroup as eg: for exception in eg.exceptions: - if isinstance(exception, runner_client.BaseRunnerRequestException): + if isinstance(exception, runner_client.BaseRunnerRequestException) or isinstance(exception, RunnerFailedToStart): logger.error(exception.message) else: + logger.error("Unexpected exception:") logger.exception(exception) - raise RunnerFailedToStart("Failed to initialize runner") + raise RunnerFailedToStart("Failed to initialize runner(s). See previous logs for more details") async def start_runner(project_def: domain.Project, env_name: str, ws_context: context.WorkspaceContext) -> runner_info.ExtensionRunnerInfo: runner = await start_extension_runner(runner_dir=project_def.dir_path, env_name=env_name, ws_context=ws_context) if runner is None: - raise RunnerFailedToStart("Runner failed to start") + raise RunnerFailedToStart(f"Runner '{env_name}' in project {project_def.name} failed to start") save_runner_in_context(runner=runner, ws_context=ws_context) @@ -301,11 +306,14 @@ async def start_runner(project_def: domain.Project, env_name: str, ws_context: c await _init_lsp_client(runner=runner, project=project_def) if project_def.dir_path not in ws_context.ws_projects_raw_configs or project_def.actions is None: - await read_configs.read_project_config(project=project_def, ws_context=ws_context) - collect_actions.collect_actions( - project_path=project_def.dir_path, ws_context=ws_context - ) - + try: + await read_configs.read_project_config(project=project_def, ws_context=ws_context) + collect_actions.collect_actions( + project_path=project_def.dir_path, ws_context=ws_context + ) + except config_models.ConfigurationError as exception: + raise RunnerFailedToStart(f"Found problem in configuration of {project_def.dir_path}: {exception.message}") + await _update_runner_config(runner=runner, project=project_def) await _finish_runner_init(runner=runner, project=project_def, ws_context=ws_context) @@ -461,3 +469,10 @@ async def check_runner(runner_dir: Path, env_name: str) -> bool: stdout = raw_stdout.decode() return 'FineCode Extension Runner ' in stdout + + +def remove_runner_venv(runner_dir: Path, env_name: str) -> None: + venv_dir_path = finecode_cmd.get_venv_dir_path(project_path=runner_dir, env_name=env_name) + if venv_dir_path.exists(): + logger.debug(f"Remove venv {venv_dir_path}") + shutil.rmtree(venv_dir_path) diff --git a/src/finecode/runner/runner_client.py b/src/finecode/runner/runner_client.py index f4c9b40..931f833 100644 --- a/src/finecode/runner/runner_client.py +++ b/src/finecode/runner/runner_client.py @@ -31,6 +31,9 @@ class NoResponse(BaseRunnerRequestException): ... class ResponseTimeout(BaseRunnerRequestException): ... +class ActionRunFailed(BaseRunnerRequestException): ... + + async def log_process_log_streams(process: asyncio.subprocess.Process) -> None: stdout, stderr = await process.communicate() @@ -175,6 +178,9 @@ async def run_action( ), timeout=None, ) + + if hasattr(response, 'error'): + raise ActionRunFailed(response.error) return_code = response.return_code raw_result = "" diff --git a/src/finecode/services.py b/src/finecode/services.py index a9b080d..186ef6e 100644 --- a/src/finecode/services.py +++ b/src/finecode/services.py @@ -161,7 +161,7 @@ async def _run_action_in_env_runner( options={"result_format": result_format}, ) except runner_client.BaseRunnerRequestException as error: - await user_messages.error(f"Action {action_name} failed: {error.message}") - raise ActionRunFailed(f"Action {action_name} failed: {error.message}") - + await user_messages.error(f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message}") + raise ActionRunFailed(f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message}") + return response From bbe1d979bf217151ed0c55da9781b7d4d66b9466 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Sun, 24 Aug 2025 08:31:46 +0200 Subject: [PATCH 10/46] Add env config in WM. Remove unused poetry locks. Fix pip command in install_deps_in_env handler. Finish prepare_runners and prepare_envs actions. Add runner config in ER. Improve logs. --- .../fine_python_ast/__init__.py | 2 +- extensions/fine_python_ast/poetry.lock | 195 --- extensions/fine_python_ast/pyproject.toml | 9 +- extensions/fine_python_ast/setup.py | 58 + extensions/fine_python_flake8/poetry.lock | 290 ---- extensions/fine_python_flake8/pyproject.toml | 9 +- extensions/fine_python_isort/poetry.lock | 210 --- extensions/fine_python_isort/pyproject.toml | 7 +- .../fine_python_module_exports/poetry.lock | 210 --- .../fine_python_module_exports/pyproject.toml | 9 +- .../fine_python_module_exports/setup.py | 58 + extensions/fine_python_mypy/poetry.lock | 260 ---- extensions/fine_python_mypy/pyproject.toml | 7 +- extensions/fine_python_pip/setup.py | 2 +- .../install_deps_in_env_handler.py | 54 +- .../fine_python_pip/prepare_env_handler.py | 48 - extensions/fine_python_virtualenv/setup.py | 2 +- .../_services/run_action.py | 57 +- .../action_handlers/__init__.py | 3 + .../dependency_config_utils.py | 63 + .../action_handlers/dump_config.py | 37 - .../prepare_envs_install_deps.py | 12 +- .../prepare_envs_read_configs.py | 5 + ...pare_runners_install_runner_and_presets.py | 119 +- .../prepare_runners_read_configs.py | 40 + .../finecode_extension_runner/di/bootstrap.py | 5 +- .../src/finecode_extension_runner/domain.py | 2 + .../finecode_extension_runner/lsp_server.py | 31 +- .../src/finecode_extension_runner/schemas.py | 1 + .../src/finecode_extension_runner/services.py | 3 +- poetry.lock | 1196 ----------------- src/finecode/base_config.toml | 106 +- src/finecode/cli.py | 5 +- src/finecode/cli_app/prepare_envs.py | 11 +- src/finecode/cli_app/run.py | 22 +- src/finecode/config/collect_actions.py | 21 +- src/finecode/config/config_models.py | 13 +- src/finecode/config/read_configs.py | 162 ++- src/finecode/domain.py | 21 +- src/finecode/runner/manager.py | 31 +- src/finecode/runner/runner_client.py | 31 +- src/finecode/services.py | 5 +- 42 files changed, 733 insertions(+), 2699 deletions(-) delete mode 100644 extensions/fine_python_ast/poetry.lock create mode 100644 extensions/fine_python_ast/setup.py delete mode 100644 extensions/fine_python_flake8/poetry.lock delete mode 100644 extensions/fine_python_isort/poetry.lock delete mode 100644 extensions/fine_python_module_exports/poetry.lock create mode 100644 extensions/fine_python_module_exports/setup.py delete mode 100644 extensions/fine_python_mypy/poetry.lock delete mode 100644 extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py create mode 100644 finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py create mode 100644 finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py delete mode 100644 poetry.lock diff --git a/extensions/fine_python_ast/fine_python_ast/__init__.py b/extensions/fine_python_ast/fine_python_ast/__init__.py index 369f6e6..11e60c2 100644 --- a/extensions/fine_python_ast/fine_python_ast/__init__.py +++ b/extensions/fine_python_ast/fine_python_ast/__init__.py @@ -1,5 +1,5 @@ -from .iast_provider import IPythonSingleAstProvider from .ast_provider import PythonSingleAstProvider +from .iast_provider import IPythonSingleAstProvider __all__ = [ "IPythonSingleAstProvider", diff --git a/extensions/fine_python_ast/poetry.lock b/extensions/fine_python_ast/poetry.lock deleted file mode 100644 index 142ee8b..0000000 --- a/extensions/fine_python_ast/poetry.lock +++ /dev/null @@ -1,195 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "pydantic" -version = "2.11.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, - {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.0" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, - {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "241dec2b0848003a1d8b7850c58ca611633cac908f00077dc383d8a223a499df" diff --git a/extensions/fine_python_ast/pyproject.toml b/extensions/fine_python_ast/pyproject.toml index a19fde1..0e03fb3 100644 --- a/extensions/fine_python_ast/pyproject.toml +++ b/extensions/fine_python_ast/pyproject.toml @@ -1,13 +1,8 @@ [project] name = "fine-python-ast" -version = "0.1.0" +version = "0.1.1" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.0"] - - -[build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +dependencies = ["finecode_extension_api==0.1.3"] diff --git a/extensions/fine_python_ast/setup.py b/extensions/fine_python_ast/setup.py new file mode 100644 index 0000000..dffa31f --- /dev/null +++ b/extensions/fine_python_ast/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="fine_python_ast", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/extensions/fine_python_flake8/poetry.lock b/extensions/fine_python_flake8/poetry.lock deleted file mode 100644 index b403632..0000000 --- a/extensions/fine_python_flake8/poetry.lock +++ /dev/null @@ -1,290 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "fine-python-ast" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_ast-0.1.0-py3-none-any.whl", hash = "sha256:3dcdcdc40ed89e0b90686f2ad5d358d8edba622a5de4c1f52f2f0787dcc4e07d"}, - {file = "fine_python_ast-0.1.0.tar.gz", hash = "sha256:ebc68aef7d0379f8771f610e436f6075b7c299a8895f1b9b7769fd038f95397e"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "flake8" -version = "7.2.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, - {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.13.0,<2.14.0" -pyflakes = ">=3.3.0,<3.4.0" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "pycodestyle" -version = "2.13.0" -description = "Python style guide checker" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, - {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, -] - -[[package]] -name = "pydantic" -version = "2.11.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, - {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pyflakes" -version = "3.3.2" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, - {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, -] - -[[package]] -name = "types-flake8" -version = "7.2.0.20250330" -description = "Typing stubs for flake8" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "types_flake8-7.2.0.20250330-py3-none-any.whl", hash = "sha256:af31590a269586309b80a439c94e59359e139e17475a8fc8d3c426ab01a40547"}, - {file = "types_flake8-7.2.0.20250330.tar.gz", hash = "sha256:481e5c914a26fabd23e85704ad055f2716f9238740bfe6fe77259c2533cb970c"}, -] - -[package.dependencies] -types-pyflakes = "*" - -[[package]] -name = "types-pyflakes" -version = "3.3.2.20250511" -description = "Typing stubs for pyflakes" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "types_pyflakes-3.3.2.20250511-py3-none-any.whl", hash = "sha256:85802fdd0b64d3553ef12ac0ba02d85c4bbd38747579c544e6bb005ec455becf"}, - {file = "types_pyflakes-3.3.2.20250511.tar.gz", hash = "sha256:d0ef58f9ec15eab2a9e427814f48587be4eb2752a8ae7dec201d65086f50ace2"}, -] - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.0" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, - {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "261b27c50864ea7935982f6a86aa5fcd65dc20e213fc2c0745e4ae4757f779ea" diff --git a/extensions/fine_python_flake8/pyproject.toml b/extensions/fine_python_flake8/pyproject.toml index 639994c..e91def9 100644 --- a/extensions/fine_python_flake8/pyproject.toml +++ b/extensions/fine_python_flake8/pyproject.toml @@ -6,13 +6,8 @@ authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" dependencies = [ - "finecode_extension_api==0.1.0", - "fine_python_ast==0.1.0", + "finecode_extension_api==0.1.*", + "fine_python_ast==0.1.*", "types-flake8 (>=7.1.0.20241020,<8.0.0.0)", "flake8 (>=7.1.2,<8.0.0)", ] - - -[build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/extensions/fine_python_isort/poetry.lock b/extensions/fine_python_isort/poetry.lock deleted file mode 100644 index 226b418..0000000 --- a/extensions/fine_python_isort/poetry.lock +++ /dev/null @@ -1,210 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -groups = ["main"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "pydantic" -version = "2.11.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, - {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.0" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, - {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[metadata] -lock-version = "2.1" -python-versions = ">= 3.11, < 3.14" -content-hash = "672ef3c1264ef102e64a40d92c5889fce13566cd0db5680064d7ae3e5ead2f43" diff --git a/extensions/fine_python_isort/pyproject.toml b/extensions/fine_python_isort/pyproject.toml index 8330470..6eadc9b 100644 --- a/extensions/fine_python_isort/pyproject.toml +++ b/extensions/fine_python_isort/pyproject.toml @@ -5,9 +5,4 @@ description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">= 3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.0", "isort (>=5.13, <6)"] - - -[build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +dependencies = ["finecode_extension_api==0.1.*", "isort (>=5.13, <6)"] diff --git a/extensions/fine_python_module_exports/poetry.lock b/extensions/fine_python_module_exports/poetry.lock deleted file mode 100644 index 1e1d351..0000000 --- a/extensions/fine_python_module_exports/poetry.lock +++ /dev/null @@ -1,210 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "fine-python-ast" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_ast-0.1.0-py3-none-any.whl", hash = "sha256:3dcdcdc40ed89e0b90686f2ad5d358d8edba622a5de4c1f52f2f0787dcc4e07d"}, - {file = "fine_python_ast-0.1.0.tar.gz", hash = "sha256:ebc68aef7d0379f8771f610e436f6075b7c299a8895f1b9b7769fd038f95397e"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "pydantic" -version = "2.11.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, - {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.0" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, - {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[metadata] -lock-version = "2.1" -python-versions = ">= 3.11, < 3.14" -content-hash = "c132c8ce7ed900d804a376b86b429d1ac6933dd438a845ab01df81adc2d1f5a1" diff --git a/extensions/fine_python_module_exports/pyproject.toml b/extensions/fine_python_module_exports/pyproject.toml index 6f4b139..1d1ffe5 100644 --- a/extensions/fine_python_module_exports/pyproject.toml +++ b/extensions/fine_python_module_exports/pyproject.toml @@ -1,13 +1,8 @@ [project] name = "fine-python-module-exports" -version = "0.1.0" +version = "0.1.1" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">= 3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.0", "fine_python_ast==0.1.0"] - - -[build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +dependencies = ["finecode_extension_api==0.1.3", "fine_python_ast==0.1.1"] diff --git a/extensions/fine_python_module_exports/setup.py b/extensions/fine_python_module_exports/setup.py new file mode 100644 index 0000000..6ce4b0e --- /dev/null +++ b/extensions/fine_python_module_exports/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="fine_python_module_exports", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/extensions/fine_python_mypy/poetry.lock b/extensions/fine_python_mypy/poetry.lock deleted file mode 100644 index 35d7ae0..0000000 --- a/extensions/fine_python_mypy/poetry.lock +++ /dev/null @@ -1,260 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "mypy" -version = "1.15.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "pydantic" -version = "2.11.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, - {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.0" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, - {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "7aecd7613778317287f8fe4e4b589373d685ccbc3ca56a47fa8cef66ce1a5149" diff --git a/extensions/fine_python_mypy/pyproject.toml b/extensions/fine_python_mypy/pyproject.toml index 6a404c8..761c79f 100644 --- a/extensions/fine_python_mypy/pyproject.toml +++ b/extensions/fine_python_mypy/pyproject.toml @@ -5,9 +5,4 @@ description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.0", "mypy (>=1.15, <2.0)"] - - -[build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +dependencies = ["finecode_extension_api==0.1.*", "mypy (>=1.15, <2.0)"] diff --git a/extensions/fine_python_pip/setup.py b/extensions/fine_python_pip/setup.py index c9c1924..8282045 100644 --- a/extensions/fine_python_pip/setup.py +++ b/extensions/fine_python_pip/setup.py @@ -48,7 +48,7 @@ def initialize_options(self): super().initialize_options() setup( - name="finecode_extension_runner", + name="fine_python_pip", cmdclass={ 'build': CustomBuild, 'build_py': CustomBuildPy, diff --git a/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py b/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py index 5fa63d9..1b80831 100644 --- a/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py +++ b/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py @@ -30,45 +30,27 @@ async def run( venv_dir_path = payload.venv_dir_path project_dir_path = payload.project_dir_path python_executable = venv_dir_path / 'bin' / 'python' - - # split dependencies in editable and not editable because pip supports - # installation of editable only with CLI flag '-e' - editable_dependencies: list[install_deps_in_env_action.Dependency] = [] - non_editable_dependencies: list[install_deps_in_env_action.Dependency] = [] - for dependency in dependencies: - if dependency.editable: - editable_dependencies.append(dependency) - else: - non_editable_dependencies.append(dependency) - errors: list[str] = [] - # run pip processes sequentially because they are executed in the same venv, - # avoid potential concurrency problem in this way - if len(non_editable_dependencies) > 0: - cmd = self._construct_pip_install_cmd(python_executable=python_executable, dependencies=non_editable_dependencies, editable=False) - error = await self._run_pip_cmd(cmd=cmd, env_name=env_name, project_dir_path=project_dir_path) - if error is not None: - errors.append(error) - - # install editable after non-editable, because non-editable can overwrite editable if there is the same dependency - if len(editable_dependencies) > 0: - cmd = self._construct_pip_install_cmd(python_executable=python_executable, dependencies=editable_dependencies, editable=True) - error = await self._run_pip_cmd(cmd=cmd, env_name=env_name, project_dir_path=project_dir_path) - if error is not None: - errors.append(error) + cmd = self._construct_pip_install_cmd(python_executable=python_executable, dependencies=dependencies) + error = await self._run_pip_cmd(cmd=cmd, env_name=env_name, project_dir_path=project_dir_path) + if error is not None: + errors = [error] + else: + errors = [] return install_deps_in_env_action.InstallDepsInEnvRunResult(errors=errors) - def _construct_pip_install_cmd(self, python_executable: pathlib.Path, dependencies: list[install_deps_in_env_action.Dependency], editable: bool) -> str: + def _construct_pip_install_cmd(self, python_executable: pathlib.Path, dependencies: list[install_deps_in_env_action.Dependency]) -> str: install_params: str = '' - if editable: - install_params += '-e ' - + if self.config.find_links is not None: for link in self.config.find_links: - install_params += f' --find-links="{link}"' + install_params += f' --find-links="{link}" ' for dependency in dependencies: + if dependency.editable: + install_params += '-e ' + if '@ file://' in dependency.version_or_source: # dependency is specified as ' @ file://' but pip CLI supports # only 'file://' @@ -87,6 +69,16 @@ async def _run_pip_cmd(self, cmd: str, env_name: str, project_dir_path: pathlib. process = await self.command_runner.run(cmd, cwd=project_dir_path) await process.wait_for_end() if process.get_exit_code() != 0: - return f'Installation of dependencies "{cmd}" in env {env_name} from {project_dir_path} failed:\nstdout: {process.get_output()}\nstderr: {process.get_error_output()}' + process_stdout = process.get_output() + process_stderr = process.get_error_output() + logs = '' + if len(process_stdout) > 0 and len(process_stderr) > 0: + logs = f'stdout: {process_stdout}\nstderr: {process_stderr}' + elif len(process_stdout) > 0: + logs = process_stdout + else: + logs = process_stderr + + return f'Installation of dependencies "{cmd}" in env {env_name} from {project_dir_path} failed:\n{logs}' return None \ No newline at end of file diff --git a/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py b/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py deleted file mode 100644 index 35ab359..0000000 --- a/extensions/fine_python_pip/src/fine_python_pip/prepare_env_handler.py +++ /dev/null @@ -1,48 +0,0 @@ -import asyncio -import dataclasses - -from finecode_extension_api import code_action -from finecode_extension_api.actions import prepare_envs as prepare_envs_action -from finecode_extension_api.interfaces import (icommandrunner, ilogger) - - -@dataclasses.dataclass -class PipPrepareEnvHandlerConfig(code_action.ActionHandlerConfig): - ... - - -class PipPrepareEnvHandler( - code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, PipPrepareEnvHandlerConfig] -): - def __init__(self, command_runner: icommandrunner.ICommandRunner, logger: ilogger.ILogger) -> None: - self.command_runner = command_runner - self.logger = logger - - async def run( - self, - payload: prepare_envs_action.PrepareEnvsRunPayload, - run_context: prepare_envs_action.PrepareEnvsRunContext, - ) -> prepare_envs_action.PrepareEnvsRunResult: - install_processes: list[icommandrunner.IAsyncProcess] = [] - for env_info in payload.envs: - python_executable = env_info.venv_dir_path / 'bin' / 'python' - project_def_path = run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] - pip_params = '' - if env_info.name == 'runtime': - pip_params += ' -e .' - - process = await self.command_runner.run(f'{python_executable} -m pip --disable-pip-version-check install {pip_params} --group="{env_info.name}"', cwd=project_def_path.parent) - install_processes.append(process) - - async with asyncio.TaskGroup() as tg: - for process in install_processes: - tg.create_task(process.wait_for_end()) - - errors: list[str] = [] - for idx, process in enumerate(install_processes): - if process.get_exit_code() != 0: - env_info = payload.envs[idx] - project_def_path = run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] - errors.append(f'Installation of dependencies in env {env_info.name} from {project_def_path} failed:\nstdout: {process.get_output()}\nstderr: {process.get_error_output()}') - - return prepare_envs_action.PrepareEnvsRunResult(errors=errors) diff --git a/extensions/fine_python_virtualenv/setup.py b/extensions/fine_python_virtualenv/setup.py index c9c1924..af096fe 100644 --- a/extensions/fine_python_virtualenv/setup.py +++ b/extensions/fine_python_virtualenv/setup.py @@ -48,7 +48,7 @@ def initialize_options(self): super().initialize_options() setup( - name="finecode_extension_runner", + name="fine_python_virtualenv", cmdclass={ 'build': CustomBuild, 'build_py': CustomBuildPy, diff --git a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py index 14eb299..8252b11 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py +++ b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py @@ -26,6 +26,11 @@ def __init__(self, message: str) -> None: self.message = message +class StopWithResponse(Exception): + def __init__(self, response: schemas.RunActionResponse) -> None: + self.response = response + + def set_partial_result_sender(send_func: typing.Callable) -> None: global partial_result_sender partial_result_sender = partial_result_sender_module.PartialResultSender( @@ -250,15 +255,34 @@ async def run_action( else: action_result.update(handler_result) + end_time = time.time_ns() + duration = (end_time - start_time) / 1_000_000 + logger.trace( + f"R{run_id} | Run action end '{request.action_name}', duration: {duration}ms" + ) + + if not isinstance(action_result, code_action.RunActionResult): + logger.error( + f"R{run_id} | Unexpected result type: {type(action_result).__name__}" + ) + raise ActionFailedException( + f"Unexpected result type: {type(action_result).__name__}" + ) + + response = action_result_to_run_action_response(action_result, options.result_format) + return response + + +def action_result_to_run_action_response(action_result: code_action.RunActionResult, asked_result_format: typing.Literal['json'] | typing.Literal['string']) -> schemas.RunActionResponse: serialized_result: dict[str, typing.Any] | str | None = None result_format = "string" run_return_code = code_action.RunReturnCode.SUCCESS if isinstance(action_result, code_action.RunActionResult): run_return_code = action_result.return_code - if options.result_format == "json": + if asked_result_format == "json": serialized_result = dataclasses.asdict(action_result) result_format = "json" - elif options.result_format == "string": + elif asked_result_format == "string": result_text = action_result.to_text() if isinstance(result_text, textstyler.StyledText): serialized_result = result_text.to_json() @@ -268,21 +292,8 @@ async def run_action( result_format = "string" else: raise ActionFailedException( - f"Unsupported result format: {options.result_format}" + f"Unsupported result format: {asked_result_format}" ) - elif action_result is not None: - logger.error( - f"R{run_id} | Unexpected result type: {type(action_result).__name__}" - ) - raise ActionFailedException( - f"Unexpected result type: {type(action_result).__name__}" - ) - - end_time = time.time_ns() - duration = (end_time - start_time) / 1_000_000 - logger.trace( - f"R{run_id} | Run action end '{request.action_name}', duration: {duration}ms" - ) return schemas.RunActionResponse( result=serialized_result, format=result_format, @@ -290,7 +301,6 @@ async def run_action( ) - def create_action_exec_info(action: domain.Action) -> domain.ActionExecInfo: try: action_type_def = run_utils.import_module_member_by_source_str(action.source) @@ -382,7 +392,12 @@ async def execute_action_handler( f"Import of action handler '{handler.name}' failed(Run {run_id}): {handler.source}" ) - handler_raw_config = handler.config + handler_global_config = runner_context.project.action_handler_configs.get(handler.source, None) + handler_raw_config = {} + # TODO: deep merge instead? + if handler_global_config is not None: + handler_raw_config.update(handler_global_config) + handler_raw_config.update(handler.config) def get_handler_config(param_type): # TODO: validation errors @@ -464,7 +479,11 @@ def get_run_context(param_type): else: execution_result = call_result except Exception as exception: - if isinstance(exception, iactionrunner.BaseRunActionException) or isinstance(exception, code_action.ActionFailedException): + if isinstance(exception, code_action.StopActionRunWithResult): + action_result = exception.result + response = action_result_to_run_action_response(action_result, 'string') + raise StopWithResponse(response=response) + elif isinstance(exception, iactionrunner.BaseRunActionException) or isinstance(exception, code_action.ActionFailedException): error_str = exception.message else: logger.error("Unhandled exception in action handler:") diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py index 94f768f..9648fba 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py @@ -4,6 +4,7 @@ from .prepare_envs_read_configs import PrepareEnvsReadConfigsHandler from .prepare_runners_dump_configs import PrepareRunnersDumpConfigsHandler from .prepare_runners_install_runner_and_presets import PrepareRunnersInstallRunnerAndPresetsHandler +from .prepare_runners_read_configs import PrepareRunnersReadConfigsHandler from .dump_config_save import DumpConfigSaveHandler __all__ = [ @@ -11,7 +12,9 @@ 'PrepareEnvsDumpConfigsHandler', 'PrepareEnvsInstallDepsHandler', 'PrepareEnvsReadConfigsHandler', + # not used 'PrepareRunnersDumpConfigsHandler', 'PrepareRunnersInstallRunnerAndPresetsHandler', + 'PrepareRunnersReadConfigsHandler', 'DumpConfigSaveHandler', ] diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py new file mode 100644 index 0000000..2aaf440 --- /dev/null +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py @@ -0,0 +1,63 @@ +import pathlib +import typing + + +def make_project_config_pip_compatible(project_raw_config: dict[str, typing.Any], config_file_path: pathlib.Path) -> None: + # TODO: what to do with included groups in dependency groups? Inherit config from its env? + finecode_config = project_raw_config.get('tool', {}).get('finecode', {}) + # apply changes to dependencies from env configuration to deps groups + for env_name, env_config in finecode_config.get('env', {}).items(): + if 'dependencies' not in env_config: + continue + + env_deps_group = project_raw_config.get('dependency-groups', {}).get(env_name, []) + dependencies = env_config['dependencies'] + for dep_name, dep_params in dependencies.items(): + # handle 'path'. 'editable' cannot be handled here because dependency + # specifier doesn't support it. It will read and processed by + # `install_deps` action + if 'path' in dep_params: + # replace dependency version / source in dependency group to this path + # + # check all dependencies because it can be duplicated: e.g. as explicit + # dependency and as dependency of action handler. + dep_indexes_in_group: list[int] = [] + for idx, dep in enumerate(env_deps_group): + # check for string because dependency can be also dictionary like '{ "include-group": "runtime"}' + if isinstance(dep, str) and get_dependency_name(dep) == dep_name: + dep_indexes_in_group.append(idx) + + if len(dep_indexes_in_group) == 0: + continue + + resolved_path_to_dep = pathlib.Path(dep_params['path']) + if not resolved_path_to_dep.is_absolute(): + # resolve relative to project dir where project def file is + resolved_path_to_dep = config_file_path.parent / resolved_path_to_dep + new_dep_str_in_group = f"{dep_name} @ file://{resolved_path_to_dep.as_posix()}" + for idx in dep_indexes_in_group: + env_deps_group[idx] = new_dep_str_in_group + + +def get_dependency_name(dependency_str: str) -> str: + # simplified way for now: find the first character which is not allowed in package + # name + for idx, ch in enumerate(dependency_str): + if not ch.isalnum() and ch not in "-_": + return dependency_str[:idx] + + # dependency can consist also just of package name without version + return dependency_str + + +class FailedToGetDependencies(Exception): + def __init__(self, message:str) -> None: + self.message = message + + +def raw_dep_to_dep_dict(raw_dep: str, env_deps_config: dict) -> dict[str, str | bool]: + name = get_dependency_name(raw_dep) + version_or_source = raw_dep[len(name):] + editable = env_deps_config.get(name, {}).get('editable', False) + dep_dict = {"name": name, "version_or_source": version_or_source, "editable": editable} + return dep_dict diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py index 6d59d4d..60758b8 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py @@ -21,42 +21,5 @@ async def run( finecode_config = run_context.raw_config_dump.get('tool', {}).get('finecode', {}) if 'presets' in finecode_config: del finecode_config['presets'] - - # apply changes to dependencies from env configuration to deps groups - for env_name, env_config in finecode_config.get('env', {}).items(): - if 'dependencies' not in env_config: - continue - - env_deps_group = run_context.raw_config_dump.get('dependency-groups', {}).get(env_name, []) - dependencies = env_config['dependencies'] - for dep_name, dep_params in dependencies.items(): - # handle 'path'. 'editable' cannot be handled here because dependency - # specifier doesn't support it. It will read and processed by - # `install_deps` action - if 'path' in dep_params: - # replace dependency version / source in dependency group to this path - try: - # check for string because dependency can be also dictionary like '{ "include-group": "runtime"}' - dep_idx_in_group = next(idx for idx, dep in enumerate(env_deps_group) if isinstance(dep, str) and get_dependency_name(dep) == dep_name) - except StopIteration: - continue - - resolved_path_to_dep = pathlib.Path(dep_params['path']) - if not resolved_path_to_dep.is_absolute(): - # resolve relative to project dir where project def file is - resolved_path_to_dep = payload.source_file_path.parent / resolved_path_to_dep - new_dep_str_in_group = f"{dep_name} @ file://{resolved_path_to_dep.as_posix()}" - env_deps_group[dep_idx_in_group] = new_dep_str_in_group return dump_config_action.DumpConfigRunResult(config_dump=run_context.raw_config_dump) - - -def get_dependency_name(dependency_str: str) -> str: - # simplified way for now: find the first character which is not allowed in package - # name - for idx, ch in enumerate(dependency_str): - if not ch.isalnum() and ch not in "-_": - return dependency_str[:idx] - - # dependency can consist also just of package name without version - return dependency_str diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py index 424c98f..a4c9536 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py @@ -6,7 +6,7 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_envs as prepare_envs_action from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger -from .dump_config import get_dependency_name +from finecode_extension_runner.action_handlers import dependency_config_utils @dataclasses.dataclass @@ -59,18 +59,10 @@ async def run( def process_raw_deps(raw_deps: list, env_deps_config, dependencies, deps_groups) -> None: for raw_dep in raw_deps: if isinstance(raw_dep, str): - name = get_dependency_name(raw_dep) + name = dependency_config_utils.get_dependency_name(raw_dep) version_or_source = raw_dep[len(name):] editable = env_deps_config.get(name, {}).get('editable', False) dependencies.append({"name": name, "version_or_source": version_or_source, "editable": editable}) elif isinstance(raw_dep, dict) and 'include-group' in raw_dep: included_group_deps = deps_groups.get(raw_dep['include-group'], []) process_raw_deps(included_group_deps, env_deps_config, dependencies, deps_groups) - - -def raw_dep_to_dep_dict(raw_dep: str, env_deps_config: dict) -> dict[str, str | bool]: - name = get_dependency_name(raw_dep) - version_or_source = raw_dep[len(name):] - editable = env_deps_config.get(name, {}).get('editable', False) - dep_dict = {"name": name, "version_or_source": version_or_source, "editable": editable} - return dep_dict diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py index 1b353ff..3d78948 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py @@ -8,6 +8,9 @@ from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger +from finecode_extension_runner.action_handlers import dependency_config_utils + + @dataclasses.dataclass class PrepareEnvsReadConfigsHandlerConfig(code_action.ActionHandlerConfig): ... @@ -32,6 +35,8 @@ async def run( project_def_path = project_defs_pathes.pop() project_dir_path = project_def_path.parent + dependency_config_utils.make_project_config_pip_compatible(project_raw_config, project_def_path) + for env_info in payload.envs: run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = project_def_path run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = project_raw_config diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py index 7a9bc41..ed411a6 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py @@ -2,12 +2,12 @@ import dataclasses import itertools import shutil +import typing from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_runners as prepare_runners_action from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger -from .dump_config import get_dependency_name -from .prepare_envs_install_deps import raw_dep_to_dep_dict +from finecode_extension_runner.action_handlers import dependency_config_utils @dataclasses.dataclass @@ -29,62 +29,89 @@ async def run( # install all these packages envs = payload.envs + dependencies_by_env: dict[str, list[dict]] = {} + for env in envs: + project_def = run_context.project_def_by_venv_dir_path[env.venv_dir_path] + project_def_path = run_context.project_def_path_by_venv_dir_path[env.venv_dir_path] + try: + dependencies = get_dependencies_in_project_raw_config(project_def, env.name) + except FailedToGetDependencies as exception: + raise code_action.ActionFailedException(f"Failed to get dependencies of env {env.name} in {project_def_path} (install_runner_and_presets handler)") + dependencies_by_env[env.name] = dependencies + install_deps_tasks: list[asyncio.Task] = [] try: async with asyncio.TaskGroup() as tg: for env in envs: - project_def = run_context.project_def_by_venv_dir_path[env.venv_dir_path] - - presets_in_config = project_def.get('tool', {}).get('finecode', {}).get('presets', []) - presets_packages_names: list[str] = [] - for preset_def in presets_in_config: - try: - preset_package = preset_def.get('source') - except KeyError: - # workspace manager validates configuration and source should - # always exist, but still handle - raise code_action.ActionFailedException(f"preset has no source: {preset_def} in {run_context.project_def_path_by_venv_dir_path[env.venv_dir_path]}") - presets_packages_names.append(preset_package) - - # straightforward solution for now - deps_groups = project_def.get('dependency-groups', {}) - env_raw_deps = deps_groups.get(env.name, []) - env_deps_config = project_def.get('tool', {}).get('finecode', {}).get('env', {}).get(env.name, {}).get('dependencies', {}) - dependencies = [] - - try: - runner_dep = next(dep for dep in env_raw_deps if isinstance(dep, str) and get_dependency_name(dep) == 'finecode_extension_runner') - except StopIteration: - raise code_action.ActionFailedException(f"prepare_runners expects finecode_extension_runner dependency in each environment, but it was not found in {env.name} (install_runner_and_presets handler)") - - runner_dep_dict = raw_dep_to_dep_dict(raw_dep=runner_dep, env_deps_config=env_deps_config) - dependencies.append(runner_dep_dict) - - for preset_package in presets_packages_names: - try: - preset_dep = next(dep for dep in env_raw_deps if isinstance(dep, str) and get_dependency_name(dep) == preset_package) - except StopIteration: - if env.name == 'dev_no_runtime': - # all preset packages must be in 'dev_no_runtime' env - raise code_action.ActionFailedException(f"'{preset_package}' is used as preset source, but not declared in 'dev_no_runtime' dependency group") - else: - continue - - preset_dep_dict = raw_dep_to_dep_dict(raw_dep=preset_dep, env_deps_config=env_deps_config) - dependencies.append(preset_dep_dict) - task = tg.create_task(self.action_runner.run_action(name='install_deps_in_env', payload={ "env_name": env.name, "venv_dir_path": env.venv_dir_path, "project_dir_path": env.project_def_path.parent, - "dependencies": dependencies + "dependencies": dependencies_by_env[env.name] })) install_deps_tasks.append(task) except ExceptionGroup as eg: - error_str = '. '.join([str(exception) for exception in eg.exceptions]) - raise code_action.ActionFailedException(error_str) + errors: list[str] = [] + for exception in eg.exceptions: + if isinstance(exception, iactionrunner.BaseRunActionException): + errors.append(exception.message) + else: + # unexpected exception + error_str = '. '.join([str(exception) for exception in eg.exceptions]) + raise code_action.ActionFailedException(error_str) + + result = prepare_runners_action.PrepareRunnersRunResult(errors=errors) + raise code_action.StopActionRunWithResult(result=result) install_deps_results = [task.result() for task in install_deps_tasks] errors: list[str] = list(itertools.chain.from_iterable([result['errors'] for result in install_deps_results])) + result = prepare_runners_action.PrepareRunnersRunResult(errors=errors) + + return result + + +class FailedToGetDependencies(Exception): + def __init__(self, message:str) -> None: + self.message = message + - return prepare_runners_action.PrepareRunnersRunResult(errors=errors) +def get_dependencies_in_project_raw_config(project_raw_config: dict[str, typing.Any], env_name: str): + # returns dependencies: presets and extension runner + presets_in_config = project_raw_config.get('tool', {}).get('finecode', {}).get('presets', []) + presets_packages_names: list[str] = [] + for preset_def in presets_in_config: + try: + preset_package = preset_def.get('source') + except KeyError: + # workspace manager validates configuration and source should + # always exist, but still handle + raise FailedToGetDependencies(f"preset has no source: {preset_def}") + presets_packages_names.append(preset_package) + + # straightforward solution for now + deps_groups = project_raw_config.get('dependency-groups', {}) + env_raw_deps = deps_groups.get(env_name, []) + env_deps_config = project_raw_config.get('tool', {}).get('finecode', {}).get('env', {}).get(env_name, {}).get('dependencies', {}) + dependencies = [] + + try: + runner_dep = next(dep for dep in env_raw_deps if isinstance(dep, str) and dependency_config_utils.get_dependency_name(dep) == 'finecode_extension_runner') + except StopIteration: + raise FailedToGetDependencies(f"prepare_runners expects finecode_extension_runner dependency in each environment, but it was not found in {env_name}") + + runner_dep_dict = dependency_config_utils.raw_dep_to_dep_dict(raw_dep=runner_dep, env_deps_config=env_deps_config) + dependencies.append(runner_dep_dict) + + for preset_package in presets_packages_names: + try: + preset_dep = next(dep for dep in env_raw_deps if isinstance(dep, str) and dependency_config_utils.get_dependency_name(dep) == preset_package) + except StopIteration: + if env_name == 'dev_no_runtime': + # all preset packages must be in 'dev_no_runtime' env + raise FailedToGetDependencies(f"'{preset_package}' is used as preset source, but not declared in 'dev_no_runtime' dependency group") + else: + continue + + preset_dep_dict = dependency_config_utils.raw_dep_to_dep_dict(raw_dep=preset_dep, env_deps_config=env_deps_config) + dependencies.append(preset_dep_dict) + return dependencies \ No newline at end of file diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py new file mode 100644 index 0000000..85f66c3 --- /dev/null +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py @@ -0,0 +1,40 @@ +import dataclasses +import shutil + +from finecode_extension_api import code_action +from finecode_extension_api.actions import prepare_runners as prepare_runners_action +from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger + +from finecode_extension_runner.action_handlers import dependency_config_utils + +@dataclasses.dataclass +class PrepareRunnersReadConfigsHandlerConfig(code_action.ActionHandlerConfig): ... + + +class PrepareRunnersReadConfigsHandler( + code_action.ActionHandler[prepare_runners_action.PrepareRunnersAction, PrepareRunnersReadConfigsHandlerConfig] +): + def __init__(self, action_runner: iactionrunner.IActionRunner, project_info_provider: iprojectinfoprovider.IProjectInfoProvider, logger: ilogger.ILogger) -> None: + self.action_runner = action_runner + self.project_info_provider = project_info_provider + self.logger = logger + + async def run( + self, payload: prepare_runners_action.PrepareRunnersRunPayload, run_context: prepare_runners_action.PrepareRunnersRunContext + ) -> prepare_runners_action.PrepareRunnersRunResult: + project_defs_pathes = set([env_info.project_def_path for env_info in payload.envs]) + if len(project_defs_pathes) != 1: + ... # TODO: error + + project_raw_config = await self.project_info_provider.get_project_raw_config() + + project_def_path = project_defs_pathes.pop() + project_dir_path = project_def_path.parent + + dependency_config_utils.make_project_config_pip_compatible(project_raw_config, project_def_path) + + for env_info in payload.envs: + run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = project_def_path + run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = project_raw_config + + return prepare_runners_action.PrepareRunnersRunResult(errors=[]) diff --git a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py index 6e511a9..00c8c03 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py +++ b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py @@ -65,11 +65,8 @@ def bootstrap(get_document_func: Callable, save_document_func: Callable): async def run_action_wrapper(action_name: str, payload: dict[str, Any]) -> dict[str, Any]: request = schemas.RunActionRequest(action_name=action_name, params=payload) options = schemas.RunActionOptions(result_format='json') + # TODO: map exceptions to iactionrunner response = await run_action.run_action(request=request, options=options) - if response.return_code != 0: - loguru_logger.get_logger().error(response.result) - # TODO: pass details - raise iactionrunner.ActionRunFailed(str(response.return_code)) return response.result diff --git a/finecode_extension_runner/src/finecode_extension_runner/domain.py b/finecode_extension_runner/src/finecode_extension_runner/domain.py index 50a1f4c..fd5b97c 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/domain.py +++ b/finecode_extension_runner/src/finecode_extension_runner/domain.py @@ -35,10 +35,12 @@ def __init__( name: str, path: Path, actions: dict[str, Action], + action_handler_configs: dict[str, dict[str, typing.Any]] ) -> None: self.name = name self.path = path self.actions = actions + self.action_handler_configs = action_handler_configs def __str__(self) -> str: return f'Project(name="{self.name}", path="{self.path}")' diff --git a/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py index 4607b3b..4c50383 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py +++ b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py @@ -169,7 +169,9 @@ async def update_config(ls: lsp_server.LanguageServer, params): try: working_dir = params[0] project_name = params[1] - actions = params[2] + config = params[2] + actions = config['actions'] + action_handler_configs = config['action_handler_configs'] request = schemas.UpdateConfigRequest( working_dir=working_dir, @@ -190,6 +192,7 @@ async def update_config(ls: lsp_server.LanguageServer, params): ) for action in actions }, + action_handler_configs=action_handler_configs ) response = await services.update_config(request=request) return response.to_dict() @@ -202,26 +205,32 @@ async def run_action(ls: lsp_server.LanguageServer, params): logger.trace(f"Run action: {params[0]}") request = schemas.RunActionRequest(action_name=params[0], params=params[1]) options = schemas.RunActionOptions(**params[2] if params[2] is not None else {}) + status: str = 'success' try: response = await services.run_action(request=request, options=options) except Exception as exception: - error_msg = "" - if isinstance(exception, services.ActionFailedException): - logger.error(f"Run action failed: {exception.message}") - error_msg = exception.message + if isinstance(exception, services.StopWithResponse): + status = 'stopped' + response = exception.response else: - logger.error("Unhandled exception in action run:") - logger.exception(exception) - error_msg = f'{type(exception)}: {str(exception)}' - return { - "error": error_msg - } + error_msg = "" + if isinstance(exception, services.ActionFailedException): + logger.error(f"Run action failed: {exception.message}") + error_msg = exception.message + else: + logger.error("Unhandled exception in action run:") + logger.exception(exception) + error_msg = f'{type(exception)}: {str(exception)}' + return { + "error": error_msg + } # dict key can be path, but pygls fails to handle slashes in dict keys, use strings # representation of result instead until the problem is properly solved result_str = json.dumps(response.to_dict()["result"]) return { + "status": status, "result": result_str, "format": response.format, "return_code": response.return_code, diff --git a/finecode_extension_runner/src/finecode_extension_runner/schemas.py b/finecode_extension_runner/src/finecode_extension_runner/schemas.py index 2289a9f..71f7fc1 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/schemas.py +++ b/finecode_extension_runner/src/finecode_extension_runner/schemas.py @@ -29,6 +29,7 @@ class UpdateConfigRequest(BaseSchema): working_dir: Path project_name: str actions: dict[str, Action] + action_handler_configs: dict[str, dict[str, Any]] @dataclass diff --git a/finecode_extension_runner/src/finecode_extension_runner/services.py b/finecode_extension_runner/src/finecode_extension_runner/services.py index 4ea1acd..c9a8863 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/services.py +++ b/finecode_extension_runner/src/finecode_extension_runner/services.py @@ -15,7 +15,7 @@ from finecode_extension_runner import project_dirs, run_utils, schemas from finecode_extension_api import code_action, textstyler from finecode_extension_runner._services import run_action as run_action_module -from finecode_extension_runner._services.run_action import run_action, ActionFailedException +from finecode_extension_runner._services.run_action import run_action, ActionFailedException, StopWithResponse from finecode_extension_runner.di import bootstrap as di_bootstrap @@ -61,6 +61,7 @@ async def update_config( name=request.project_name, path=project_path, actions=actions, + action_handler_configs=request.action_handler_configs ), ) diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 63d038e..0000000 --- a/poetry.lock +++ /dev/null @@ -1,1196 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.9.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, - {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" -typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} - -[package.extras] -doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""] -trio = ["trio (>=0.26.1)"] - -[[package]] -name = "attrs" -version = "25.3.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, -] - -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] - -[[package]] -name = "black" -version = "25.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "cattrs" -version = "24.1.3" -description = "Composable complex class support for attrs and dataclasses." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5"}, - {file = "cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff"}, -] - -[package.dependencies] -attrs = ">=23.1.0" - -[package.extras] -bson = ["pymongo (>=4.4.0)"] -cbor2 = ["cbor2 (>=5.4.6)"] -msgpack = ["msgpack (>=1.0.5)"] -msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] -orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] -pyyaml = ["pyyaml (>=6.0)"] -tomlkit = ["tomlkit (>=0.11.8)"] -ujson = ["ujson (>=5.7.0)"] - -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev"] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -markers = {main = "sys_platform == \"win32\" or platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} - -[[package]] -name = "debugpy" -version = "1.8.14" -description = "An implementation of the Debug Adapter Protocol for Python" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "debugpy-1.8.14-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:93fee753097e85623cab1c0e6a68c76308cd9f13ffdf44127e6fab4fbf024339"}, - {file = "debugpy-1.8.14-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d937d93ae4fa51cdc94d3e865f535f185d5f9748efb41d0d49e33bf3365bd79"}, - {file = "debugpy-1.8.14-cp310-cp310-win32.whl", hash = "sha256:c442f20577b38cc7a9aafecffe1094f78f07fb8423c3dddb384e6b8f49fd2987"}, - {file = "debugpy-1.8.14-cp310-cp310-win_amd64.whl", hash = "sha256:f117dedda6d969c5c9483e23f573b38f4e39412845c7bc487b6f2648df30fe84"}, - {file = "debugpy-1.8.14-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:1b2ac8c13b2645e0b1eaf30e816404990fbdb168e193322be8f545e8c01644a9"}, - {file = "debugpy-1.8.14-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf431c343a99384ac7eab2f763980724834f933a271e90496944195318c619e2"}, - {file = "debugpy-1.8.14-cp311-cp311-win32.whl", hash = "sha256:c99295c76161ad8d507b413cd33422d7c542889fbb73035889420ac1fad354f2"}, - {file = "debugpy-1.8.14-cp311-cp311-win_amd64.whl", hash = "sha256:7816acea4a46d7e4e50ad8d09d963a680ecc814ae31cdef3622eb05ccacf7b01"}, - {file = "debugpy-1.8.14-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:8899c17920d089cfa23e6005ad9f22582fd86f144b23acb9feeda59e84405b84"}, - {file = "debugpy-1.8.14-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6bb5c0dcf80ad5dbc7b7d6eac484e2af34bdacdf81df09b6a3e62792b722826"}, - {file = "debugpy-1.8.14-cp312-cp312-win32.whl", hash = "sha256:281d44d248a0e1791ad0eafdbbd2912ff0de9eec48022a5bfbc332957487ed3f"}, - {file = "debugpy-1.8.14-cp312-cp312-win_amd64.whl", hash = "sha256:5aa56ef8538893e4502a7d79047fe39b1dae08d9ae257074c6464a7b290b806f"}, - {file = "debugpy-1.8.14-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:329a15d0660ee09fec6786acdb6e0443d595f64f5d096fc3e3ccf09a4259033f"}, - {file = "debugpy-1.8.14-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f920c7f9af409d90f5fd26e313e119d908b0dd2952c2393cd3247a462331f15"}, - {file = "debugpy-1.8.14-cp313-cp313-win32.whl", hash = "sha256:3784ec6e8600c66cbdd4ca2726c72d8ca781e94bce2f396cc606d458146f8f4e"}, - {file = "debugpy-1.8.14-cp313-cp313-win_amd64.whl", hash = "sha256:684eaf43c95a3ec39a96f1f5195a7ff3d4144e4a18d69bb66beeb1a6de605d6e"}, - {file = "debugpy-1.8.14-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:d5582bcbe42917bc6bbe5c12db1bffdf21f6bfc28d4554b738bf08d50dc0c8c3"}, - {file = "debugpy-1.8.14-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5349b7c3735b766a281873fbe32ca9cca343d4cc11ba4a743f84cb854339ff35"}, - {file = "debugpy-1.8.14-cp38-cp38-win32.whl", hash = "sha256:7118d462fe9724c887d355eef395fae68bc764fd862cdca94e70dcb9ade8a23d"}, - {file = "debugpy-1.8.14-cp38-cp38-win_amd64.whl", hash = "sha256:d235e4fa78af2de4e5609073972700523e372cf5601742449970110d565ca28c"}, - {file = "debugpy-1.8.14-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:413512d35ff52c2fb0fd2d65e69f373ffd24f0ecb1fac514c04a668599c5ce7f"}, - {file = "debugpy-1.8.14-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c9156f7524a0d70b7a7e22b2e311d8ba76a15496fb00730e46dcdeedb9e1eea"}, - {file = "debugpy-1.8.14-cp39-cp39-win32.whl", hash = "sha256:b44985f97cc3dd9d52c42eb59ee9d7ee0c4e7ecd62bca704891f997de4cef23d"}, - {file = "debugpy-1.8.14-cp39-cp39-win_amd64.whl", hash = "sha256:b1528cfee6c1b1c698eb10b6b096c598738a8238822d218173d21c3086de8123"}, - {file = "debugpy-1.8.14-py2.py3-none-any.whl", hash = "sha256:5cd9a579d553b6cb9759a7908a41988ee6280b961f24f63336835d9418216a20"}, - {file = "debugpy-1.8.14.tar.gz", hash = "sha256:7cd287184318416850aa8b60ac90105837bb1e59531898c07569d197d2ed5322"}, -] - -[[package]] -name = "fine-python-aksem" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_module_exports = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_module_exports"} - -[package.source] -type = "git" -url = "https://github.com/Aksem/fine_python_aksem.git" -reference = "HEAD" -resolved_reference = "56ce22c3fb1228a6e85d1a8727a0fa74787608d9" - -[[package]] -name = "fine-python-ast" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "extensions/fine_python_ast" - -[[package]] -name = "fine-python-black" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -black = ">=25.1.0,<26.0.0" -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "extensions/fine_python_black" - -[[package]] -name = "fine-python-flake8" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_ast = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_ast"} -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} -flake8 = ">=7.1.2,<8.0.0" -types-flake8 = ">=7.1.0.20241020,<8.0.0.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "extensions/fine_python_flake8" - -[[package]] -name = "fine-python-format" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_black = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_black"} -fine_python_isort = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_isort"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "presets/fine_python_format" - -[[package]] -name = "fine-python-import-linter" -version = "0.1.0" -description = "" -optional = false -python-versions = ">= 3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} -import-linter = ">=2.1,<3.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "extensions/fine_python_import_linter" - -[[package]] -name = "fine-python-isort" -version = "0.1.0" -description = "" -optional = false -python-versions = ">= 3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} -isort = ">=5.13,<6" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "extensions/fine_python_isort" - -[[package]] -name = "fine-python-lint" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_flake8 = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_flake8"} -fine_python_mypy = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_mypy"} -flake8-bugbear = ">=24.12.12,<25.0.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "presets/fine_python_lint" - -[[package]] -name = "fine-python-module-exports" -version = "0.1.0" -description = "" -optional = false -python-versions = ">= 3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_ast = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_ast"} -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "extensions/fine_python_module_exports" - -[[package]] -name = "fine-python-mypy" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} -mypy = ">=1.15,<2.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "extensions/fine_python_mypy" - -[[package]] -name = "fine-python-recommended" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_format = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_format"} -fine_python_lint = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_lint"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "85ba26049dba2d4792f6e9d5370a1f5eef992ec8" -subdirectory = "presets/fine_python_recommended" - -[[package]] -name = "finecode-dev-common-preset" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = true - -[package.dependencies] -fine_python_aksem = {git = "https://github.com/Aksem/fine_python_aksem.git"} -fine_python_recommended = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_recommended"} - -[package.source] -type = "directory" -url = "finecode_dev_common_preset" - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "flake8" -version = "7.2.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, - {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.13.0,<2.14.0" -pyflakes = ">=3.3.0,<3.4.0" - -[[package]] -name = "flake8-bugbear" -version = "24.12.12" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" -groups = ["dev"] -files = [ - {file = "flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e"}, - {file = "flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -flake8 = ">=6.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "grimp" -version = "3.9" -description = "Builds a queryable graph of the imports within one or more Python packages." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "grimp-3.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:febc16712658e4eed18a8f313036165eef33dabad65afde01c1da0429923f229"}, - {file = "grimp-3.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b79782e4fad92ee0375ac20c086d7e32e23d880ff70541295da4fba07336486"}, - {file = "grimp-3.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd8480adcd5241ffb013be922885b87de2fc7d834cdba1cc1d65f2c378bd282"}, - {file = "grimp-3.9-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc5860e9ad17f8538bb2064aed86df27058187e554f21ecc79b3f5ae7bf5f919"}, - {file = "grimp-3.9-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dca743dad74db046a1104f53d83d7297eb14990d0ba4c8a548c493145cc158bc"}, - {file = "grimp-3.9-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9886231877cda72aba4c4975d819d79f149e0edb22fd3f55ed5d5f89cb3ab28e"}, - {file = "grimp-3.9-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f80f7bf293c552edf1e48dcc7a4339532e604357b22b2ee1d55d08ae1ff2a811"}, - {file = "grimp-3.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb8bb74b38727a8928cfbfa670632c1f27cb97350348f954e318243a785f7a51"}, - {file = "grimp-3.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9fec4ca6a9cde9c6bc2c565243ec4294f6dea65da34c9af6b3d2725d0e6e055e"}, - {file = "grimp-3.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:03e99fed9ac98f732bfa528eeed76643013d8278eb62a271d841fadbde408252"}, - {file = "grimp-3.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b391fe8379fade735c21a016fd22eafb85cd65c7962e8bed176dc9c2ad5a169c"}, - {file = "grimp-3.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7f2af0715304971798241353a3e6c5cc64339a4871dd7b33372c810e5c7c7251"}, - {file = "grimp-3.9-cp310-cp310-win32.whl", hash = "sha256:4db8ac24b52e0859ac47ee2c6aa1b40323774078dbc22193699b3ad7d2bb1bd2"}, - {file = "grimp-3.9-cp310-cp310-win_amd64.whl", hash = "sha256:75ca679e9d341f31af7af6b02dcb1069bc08f3746416c7d86508073254714ff4"}, - {file = "grimp-3.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f28984e7b0be7c820cb41bf6fb05d707567cc892e84ca5cd21603c57e86627dd"}, - {file = "grimp-3.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:21a03a6b1c682f98d59971f10152d936fe4def0ac48109fd72d111a024588c7a"}, - {file = "grimp-3.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae8ce8ce5535c6bd50d4ce10747afc8f3d6d98b1c25c66856219bbeae82f3156"}, - {file = "grimp-3.9-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3e26ed94b20b2991bc0742ab468d40bff0e33619cf506ecb2ec15dd8baa1094d"}, - {file = "grimp-3.9-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6efe43e54753edca1705bf2d002e0c40e86402c19cd4ea66fb71e1bb628e8da"}, - {file = "grimp-3.9-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83b2354e90b57ea3335381df78ffe0d653f68a7a9e6fcf382f157ad9558d778"}, - {file = "grimp-3.9-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:465b6814c3e94081e9b9491975d8f14583ff1b2712e9ee2c7a88d165fece33ab"}, - {file = "grimp-3.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f7d4289c3dd7fdd515abf7ad7125c405367edbee6e286f29d5176b4278a232d"}, - {file = "grimp-3.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3de8685aefa3de3c966cebcbd660cbbdb10f890b0b11746adf730b5dc738b35d"}, - {file = "grimp-3.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:aec648946dd9f9cc154aa750b6875e1e6bb2a621565b0ca98e8b4228838c971e"}, - {file = "grimp-3.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:edeb78b27cee3e484e27d91accd585bfa399870cb1097f9132a8fdc920dcc584"}, - {file = "grimp-3.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:32993eaa86d3e65d302394c09228e17373720353640c7bc6847e40cac618db9e"}, - {file = "grimp-3.9-cp311-cp311-win32.whl", hash = "sha256:0e6cc81104b227a4185a2e2644f1ee70e90686174331c3d8004848ba9c811f08"}, - {file = "grimp-3.9-cp311-cp311-win_amd64.whl", hash = "sha256:088f5a67f67491a5d4c20ef67941cbbb15f928f78a412f0d032460ee2ce518fb"}, - {file = "grimp-3.9-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c19a27aa7541b620df94ceafde89d6ebf9ee1b263e80d278ea45bdd504fec769"}, - {file = "grimp-3.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f68e7a771c9eb4459106decd6cc4f11313202b10d943a1a8bed463b528889dd0"}, - {file = "grimp-3.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8290eb4561dc29c590fc099f2bdac4827a9b86a018e146428854f9742ab480ef"}, - {file = "grimp-3.9-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4574c0d135e6af8cddc31ac9617c00aac3181bb4d476f5aea173a5f2ac8c7479"}, - {file = "grimp-3.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5e4110bd0aedd7da899e44ec0d4a93529e93f2d03e5786e3469a5f7562e11e9"}, - {file = "grimp-3.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d098f6e10c0e42c6be0eca2726a7d7218e90ba020141fa3f88426a5f7d09d71"}, - {file = "grimp-3.9-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69573ecc5cc84bb175e5aa5af2fe09dfb2f33a399c59c025f5f3d7d2f6f202fe"}, - {file = "grimp-3.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63e4bdb4382fb0afd52216e70a0e4da3f0500de8f9e40ee8d2b68a16a35c40c4"}, - {file = "grimp-3.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ddde011e9bb2fa1abb816373bd8898d1a486cf4f4b13dc46a11ddcd57406e1b"}, - {file = "grimp-3.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa32eed6fb383ec4e54b4073e8ce75a5b151bb1f1d11be66be18aee04d3c9c4b"}, - {file = "grimp-3.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e9cc09977f8688839e0c9873fd214e11c971f5df38bffb31d402d04803dfff92"}, - {file = "grimp-3.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3a732b461db86403aa3c8154ffab85d1964c8c6adaa763803ce260abbc504b6f"}, - {file = "grimp-3.9-cp312-cp312-win32.whl", hash = "sha256:829d60b4c1c8c6bfb1c7348cf3e30b87f462a7d9316ced9d8265146a2153a0cd"}, - {file = "grimp-3.9-cp312-cp312-win_amd64.whl", hash = "sha256:556ab4fbf943299fd90e467d481803b8e1a57d28c24af5867012559f51435ceb"}, - {file = "grimp-3.9-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:867b476677b1d2f89b6c9ca0d7c47b279fe9d0230087f621c6aba94331411690"}, - {file = "grimp-3.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:faf5dd2cc7012a6024e743976674d55e66c6e556eaffd30e5843a88cc4623c16"}, - {file = "grimp-3.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ff6c0de2e9cffed8f7ec1a9c80888f01017806cfb9acf9c3d8fc3137a629d51"}, - {file = "grimp-3.9-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e38f92a650756f9b00198991cb60c5e3add9d68475425fb4fe0960d1586660ce"}, - {file = "grimp-3.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e1ef77c7841b15d9f5002c767da1060ec42cb477fa7ae33d7f9dffb4705dc0"}, - {file = "grimp-3.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:19a9bb0b05d1b0738920c604cdc544c9073df6edd71f31963054576647c8f897"}, - {file = "grimp-3.9-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9f9d5e6182859900610f15704847897115707b28ca2c9b5c754ef3bef9adb485"}, - {file = "grimp-3.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e63efe9c2df2e8efe98142fa754ef9140e3aa3ce942ef55f52bb7a177a0822"}, - {file = "grimp-3.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e204b17675763a7091fd5e8b7c58c83c8383505d90b6aea6a5e0d5bb737cb856"}, - {file = "grimp-3.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:15d23a90d34d3f94e5437c7bc29ad1b82d059ed9b039c84d6ef20d83b826ca88"}, - {file = "grimp-3.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04ed7f682ac07aee6e8cd99c1ea3d0ba26ea8167b71b4b79f05640982c1b1fa3"}, - {file = "grimp-3.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75f33e7b98652ce17fc9a5d0dce0bc5f4ba68fd73a15f10dd4cd1ea511bab0c1"}, - {file = "grimp-3.9-cp313-cp313-win32.whl", hash = "sha256:72921d8727a508b34393a330748db91fca62fa506b86f5a4c457f713a6468c15"}, - {file = "grimp-3.9-cp313-cp313-win_amd64.whl", hash = "sha256:cd65bc6d030d9d788a1794e01cdc3b4abce2971cc821e2e7dc02d09c45febc56"}, - {file = "grimp-3.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:057d4f7e4b9f62909406701d5bab773b39e1fd8591043c6b19dba3ab3b275625"}, - {file = "grimp-3.9-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c660f1222b7c11d725d298bce09b85376b0084d5515b8364a7a70c0547a0992"}, - {file = "grimp-3.9-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78662f2c0ae4e7ff3eacff051e6b3110ed026135545a1825a53a858d4e966ebb"}, - {file = "grimp-3.9-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b57b20f51ce7765adaffd80b3a17a365b770a5d237a772a2a8a74cc19c186f2"}, - {file = "grimp-3.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:335511ad698e2a7d6e15dccdb843afc6ad4bde79f213479c799f67c98ce36002"}, - {file = "grimp-3.9-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:574c94895d4fcac2e5ae794636fe687fb80b9ca59fe3bb8458d7a64bc3b3ed9e"}, - {file = "grimp-3.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:84c95f9df61ddaffd8f41a4181aa652f3fdf9932b26634cd8273d4dcd926321e"}, - {file = "grimp-3.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9ddcbfd11d6e6b813121db1116f6b3c4930ab433a949522b5e80542c5da3d805"}, - {file = "grimp-3.9-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5de1aedb52351f23f7a70cff66c16feacfd69567fb1cac04a9b0ea321760f2f5"}, - {file = "grimp-3.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:44e6785aed2dcce003900f41798c21ae8c6f5899f36e304870a86a75ade18a0d"}, - {file = "grimp-3.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf998acdfa7ed87a70b0eba44cb240aec0273dc087f132c72ed1ad583625313"}, - {file = "grimp-3.9-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:751304b8de0525c1276299f72cd81df04367438cdb384ea7df98753f11e4342b"}, - {file = "grimp-3.9-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72675e36675479df1f933cff4969adb480037c0155461b6944d030bd83c46deb"}, - {file = "grimp-3.9-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a45d6042b9cabc4df9817745a531b746c0ead456550928219c7642ec05dd222"}, - {file = "grimp-3.9-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9a61fd84e01ce5d4ca150494c614cc4db2a6c974d454bca7f169dd03d52f741"}, - {file = "grimp-3.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c8bd22361a9cc5f602d9386ee271c260493dfab68a21c7f61ee5227c4407b7d"}, - {file = "grimp-3.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1d08a71a2d794f4c4fd891f96f6a37e8e83e562aa078f72eaaa3ca07ee8ab550"}, - {file = "grimp-3.9-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:f32902b833c9ef9208f2c62975ba234ab4204e7a6f79f919bde6dc2fc589531b"}, - {file = "grimp-3.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:11aafe1cbeb263a9a9f592feb957929bf7e7cef77a315ac3a13f9692eb8b16b1"}, - {file = "grimp-3.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bf316209ec7e2e46506e1ce3223b473f9869d01e88c6f01f78198434dd987bd5"}, - {file = "grimp-3.9-cp39-cp39-win32.whl", hash = "sha256:156d76ad1b2ac8967af0962909fda251e3f14a3ab2ee453a66ab12cf0186d3c4"}, - {file = "grimp-3.9-cp39-cp39-win_amd64.whl", hash = "sha256:6f66c9d037f4adeb30ea083da4e2a5d77411107ea86e488706864817e7663a76"}, - {file = "grimp-3.9-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c10a8dadb7094a4d437da37ad9c4782eb3d08c52b8e80aa9a9cfbf0d0d289203"}, - {file = "grimp-3.9-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9e86bc544056385041f9e5ff4f061fa88209219cee5689aa564d995ecb0bfe8"}, - {file = "grimp-3.9-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94414e7c29117cc6bc92a68bc2fe81ad3c80469c410c6c7da14db10fb814b66c"}, - {file = "grimp-3.9-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c637b9f58e4a4a9265b3c1db5ff30d285495412b5baeeede676443c0bd9cb75b"}, - {file = "grimp-3.9-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e10be6c463ac6f519f5b9a047e57292f00162d750c61bbb19df8d0ef23144c71"}, - {file = "grimp-3.9-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6217f7bc2a2227bd0c7baaf2c84fee2e1fa504535838a58684675fcb1d05a144"}, - {file = "grimp-3.9-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2d6a2aa5e72a6b24b1df0904e0b73be3be037ac395a0f7c41c753cd1be21d0be"}, - {file = "grimp-3.9-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:8e7dceec0c9651b3cbefe3bc59eaec6c74054d862630458db46e0be5bdbbbc85"}, - {file = "grimp-3.9-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:b6dbdb37c85d335e035db7670da118d12a9ea09662fe74a667706f6dda36d6dd"}, - {file = "grimp-3.9-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8125134fca3b89608efae89b82e2ab2156619492e26af6cb8d90e862d31b345e"}, - {file = "grimp-3.9-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b9069230b38f50fe85adba1037a8c9abfb21d2e219ba7ee76e045b3ff2b119"}, - {file = "grimp-3.9-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2bda4243558d3ddd349a135c3444ef20d6778471316bbe8e5ca84ffc7a97447"}, - {file = "grimp-3.9-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9711f25ad6c03f33c14b158c4c83beaf906d544026a575f4aadd8dbd9d30594"}, - {file = "grimp-3.9-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:642139de62d81650fcf3130a68fc1a6db2e244a2c02d84ff98e6b73d70588de1"}, - {file = "grimp-3.9-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33b819a79171d8707583b40d3fc658f16758339da19496f0a49ae856cf904104"}, - {file = "grimp-3.9-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ff48e80a2c1ffde2c0b5b6e0a1f178058090f3d0e25b3ae1f2f00a9fb38a2fe"}, - {file = "grimp-3.9-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e087b54eb1b8b6d3171d986dbfdd9ad7d73df1944dfaa55d08d3c66b33c94638"}, - {file = "grimp-3.9-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:a9c3bd888ea57dca279765078facba2d4ed460a2f19850190df6b1e5e498aef3"}, - {file = "grimp-3.9-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:5392b4f863dca505a6801af8be738228cdce5f1c71d90f7f8efba2cdc2f1a1cb"}, - {file = "grimp-3.9-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:7719cb213aacad7d0e6d69a9be4f998133d9b9ad3fa873b07dfaa221131ac2dc"}, - {file = "grimp-3.9-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39e0fdc42b55954e26c595e7c9ac477218d4a342024b198d90ab76ec82263065"}, - {file = "grimp-3.9-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99eb44ecb368c0f224247697298182db1858b1142e612a13f6e251acbe27d2ac"}, - {file = "grimp-3.9-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e4c3766190e1e21d80f368e4a8c48d306fd4ba568ad6168947d39b2d1edd029"}, - {file = "grimp-3.9-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8cb92110ded311474b90f30acef51fd23b458c89252ce5a41a1933f39ad8abf7"}, - {file = "grimp-3.9-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:badfc77336e950d3c40ff1338fe32bf603c0afebd7582cbfb34905eb4f85fca1"}, - {file = "grimp-3.9-pp39-pypy39_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1ddd50c4f62196adcb14999ec49381a41f8404ea166e4d25afb8f944ebaa1728"}, - {file = "grimp-3.9-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:b994de7d526825bbfc51d1bf1e4ce6037acf0b6a264422029d02adb591575e28"}, - {file = "grimp-3.9-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:434b53437fd06071e94db6c2979b686e14d5995dc7c2ca3fe0793ef489c427da"}, - {file = "grimp-3.9.tar.gz", hash = "sha256:b677ac17301d7e0f1e19cc7057731bd7956a2121181eb5057e51efb44301fb0a"}, -] - -[package.dependencies] -joblib = ">=1.3.0" -typing-extensions = ">=3.10.0.0" - -[[package]] -name = "idna" -version = "3.10" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "import-linter" -version = "2.3" -description = "Enforces rules for the imports within and between Python packages." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "import_linter-2.3-py3-none-any.whl", hash = "sha256:5b851776782048ff1be214f1e407ef2e3d30dcb23194e8b852772941811a1258"}, - {file = "import_linter-2.3.tar.gz", hash = "sha256:863646106d52ee5489965670f97a2a78f2c8c68d2d20392322bf0d7cc0111aa7"}, -] - -[package.dependencies] -click = ">=6" -grimp = ">=3.7" -typing-extensions = ">=3.10.0.0" - -[[package]] -name = "iniconfig" -version = "2.1.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, - {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, -] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -groups = ["dev"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "joblib" -version = "1.5.0" -description = "Lightweight pipelining with Python functions" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "joblib-1.5.0-py3-none-any.whl", hash = "sha256:206144b320246485b712fc8cc51f017de58225fa8b414a1fe1764a7231aca491"}, - {file = "joblib-1.5.0.tar.gz", hash = "sha256:d8757f955389a3dd7a23152e43bc297c2e0c2d3060056dad0feefc88a06939b5"}, -] - -[[package]] -name = "loguru" -version = "0.7.3" -description = "Python logging made (stupidly) simple" -optional = false -python-versions = "<4.0,>=3.5" -groups = ["main"] -files = [ - {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, - {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} -win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} - -[package.extras] -dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""] - -[[package]] -name = "lsprotocol" -version = "2024.0.0b1" -description = "Python types for Language Server Protocol." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "lsprotocol-2024.0.0b1-py3-none-any.whl", hash = "sha256:93785050ac155ae2be16b1ebfbd74c214feb3d3ef77b10399ce941e5ccef6ebd"}, - {file = "lsprotocol-2024.0.0b1.tar.gz", hash = "sha256:d3667fb70894d361aa6c495c5c8a1b2e6a44be65ff84c21a9cbb67ebfb4830fd"}, -] - -[package.dependencies] -attrs = ">=21.3.0" -cattrs = "!=23.2.1" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy" -version = "1.15.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "ordered-set" -version = "4.1.0" -description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, - {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, -] - -[package.extras] -dev = ["black", "mypy", "pytest"] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pluggy" -version = "1.6.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, - {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["coverage", "pytest", "pytest-benchmark"] - -[[package]] -name = "pycodestyle" -version = "2.13.0" -description = "Python style guide checker" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, - {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, -] - -[[package]] -name = "pydantic" -version = "2.10.6" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, - {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" -typing-extensions = ">=4.12.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.27.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pyflakes" -version = "3.3.2" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, - {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, -] - -[[package]] -name = "pygls" -version = "2.0.0a2" -description = "A pythonic generic language server (pronounced like 'pie glass')" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pygls-2.0.0a2-py3-none-any.whl", hash = "sha256:b202369321409343aa6440d73111d9fa0c22e580466ff1c7696b8358bb91f243"}, - {file = "pygls-2.0.0a2.tar.gz", hash = "sha256:03e00634ed8d989918268aaa4b4a0c3ab857ea2d4ee94514a52efa5ddd6d5d9f"}, -] - -[package.dependencies] -cattrs = ">=23.1.2" -lsprotocol = "2024.0.0b1" - -[package.extras] -ws = ["websockets (>=13.0)"] - -[[package]] -name = "pytest" -version = "7.4.4" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "tomlkit" -version = "0.11.8" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, - {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, -] - -[[package]] -name = "types-flake8" -version = "7.2.0.20250330" -description = "Typing stubs for flake8" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "types_flake8-7.2.0.20250330-py3-none-any.whl", hash = "sha256:af31590a269586309b80a439c94e59359e139e17475a8fc8d3c426ab01a40547"}, - {file = "types_flake8-7.2.0.20250330.tar.gz", hash = "sha256:481e5c914a26fabd23e85704ad055f2716f9238740bfe6fe77259c2533cb970c"}, -] - -[package.dependencies] -types-pyflakes = "*" - -[[package]] -name = "types-pyflakes" -version = "3.3.2.20250511" -description = "Typing stubs for pyflakes" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "types_pyflakes-3.3.2.20250511-py3-none-any.whl", hash = "sha256:85802fdd0b64d3553ef12ac0ba02d85c4bbd38747579c544e6bb005ec455becf"}, - {file = "types_pyflakes-3.3.2.20250511.tar.gz", hash = "sha256:d0ef58f9ec15eab2a9e427814f48587be4eb2752a8ae7dec201d65086f50ace2"}, -] - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "watchdog" -version = "4.0.2" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"}, - {file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"}, - {file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"}, - {file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"}, - {file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -description = "A small Python utility to set file creation time on Windows" -optional = false -python-versions = ">=3.5" -groups = ["main"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, - {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, -] - -[package.extras] -dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "3f14e2690a4711f544fa35cd2f9433c3c571df472fc8f4fd39c7b84020399470" diff --git a/src/finecode/base_config.toml b/src/finecode/base_config.toml index a35d0bc..6772ab2 100644 --- a/src/finecode/base_config.toml +++ b/src/finecode/base_config.toml @@ -1,12 +1,17 @@ [tool.finecode.action.prepare_envs] source = "finecode_extension_api.actions.prepare_envs.PrepareEnvsAction" -handlers = [ - { name = "prepare_envs_dump_configs", source = "finecode_extension_runner.action_handlers.PrepareEnvsDumpConfigsHandler", env = "dev_workspace", dependencies = [ - ] }, - { name = "prepare_envs_install_deps", source = "finecode_extension_runner.action_handlers.PrepareEnvsInstallDepsHandler", env = "dev_workspace", dependencies = [ - ] }, -] -config = {} + +[[tool.finecode.action.prepare_envs.handlers]] +name = "prepare_envs_dump_configs" +source = "finecode_extension_runner.action_handlers.PrepareEnvsReadConfigsHandler" +env = "dev_workspace" + + +[[tool.finecode.action.prepare_envs.handlers]] +name = "prepare_envs_install_deps" +source = "finecode_extension_runner.action_handlers.PrepareEnvsInstallDepsHandler" +env = "dev_workspace" + # preparing dev workspaces doesn't need dumping config for two reasons: # - dependencies in `dev_workspace` are expected to be simple and installable @@ -16,45 +21,66 @@ config = {} # exist yet [tool.finecode.action.prepare_dev_workspaces_envs] source = "finecode_extension_api.actions.prepare_envs.PrepareEnvsAction" -handlers = [ - { name = "prepare_venvs", source = "fine_python_virtualenv.VirtualenvPrepareEnvHandler", env = "dev_workspace", dependencies = [ - "fine_python_virtualenv @ file:///home/user/Development/FineCode/finecode/extensions/fine_python_virtualenv", - ] }, - { name = "prepare_envs_read_configs", source = "finecode_extension_runner.action_handlers.PrepareEnvsReadConfigsHandler", env = "dev_workspace", dependencies = [ - ] }, - { name = "prepare_envs_install_deps", source = "finecode_extension_runner.action_handlers.PrepareEnvsInstallDepsHandler", env = "dev_workspace", dependencies = [ - ] }, -] -config = {} + +[[tool.finecode.action.prepare_dev_workspaces_envs.handlers]] +name = "prepare_venvs" +source = "fine_python_virtualenv.VirtualenvPrepareEnvHandler" +env = "dev_workspace" +dependencies = ["fine_python_virtualenv==0.1.3"] + +[[tool.finecode.action.prepare_dev_workspaces_envs.handlers]] +name = "prepare_envs_read_configs" +source = "finecode_extension_runner.action_handlers.PrepareEnvsReadConfigsHandler" +env = "dev_workspace" + + +[[tool.finecode.action.prepare_dev_workspaces_envs.handlers]] +name = "prepare_envs_install_deps" +source = "finecode_extension_runner.action_handlers.PrepareEnvsInstallDepsHandler" +env = "dev_workspace" + [tool.finecode.action.prepare_runners] source = "finecode_extension_api.actions.prepare_runners.PrepareRunnersAction" -handlers = [ - { name = "prepare_runners_venvs", source = "fine_python_virtualenv.VirtualenvPrepareRunnersHandler", env = "dev_workspace", dependencies = [ - "@ file:///home/user/Development/FineCode/finecode/extensions/fine_python_virtualenv", - ] }, - { name = "prepare_runners_dump_configs", source = "finecode_extension_runner.action_handlers.PrepareRunnersDumpConfigsHandler", env = "dev_workspace", dependencies = [ - ] }, - { name = "prepare_runners_install_runner_and_presets", source = "finecode_extension_runner.action_handlers.PrepareEnvsInstallDepsHandler", env = "dev_workspace", dependencies = [ - ] }, -] -config = {} + +[[tool.finecode.action.prepare_runners.handlers]] +name = "prepare_runners_venvs" +source = "fine_python_virtualenv.VirtualenvPrepareRunnersHandler" +env = "dev_workspace" +dependencies = ["fine_python_virtualenv==0.1.3"] + +[[tool.finecode.action.prepare_runners.handlers]] +name = "prepare_runners_read_configs" +source = "finecode_extension_runner.action_handlers.PrepareRunnersReadConfigsHandler" +env = "dev_workspace" + + +[[tool.finecode.action.prepare_runners.handlers]] +name = "prepare_runners_install_runner_and_presets" +source = "finecode_extension_runner.action_handlers.PrepareRunnersInstallRunnerAndPresetsHandler" +env = "dev_workspace" + [tool.finecode.action.dump_config] source = "finecode_extension_api.actions.dump_config.DumpConfigAction" -handlers = [ - { name = "dump_config", source = "finecode_extension_runner.action_handlers.DumpConfigHandler", env = "dev_workspace", dependencies = [ - ] }, - { name = "dump_config_save", source = "finecode_extension_runner.action_handlers.DumpConfigSaveHandler", env = "dev_workspace", dependencies = [ - ] }, -] -config = {} + +[[tool.finecode.action.dump_config.handlers]] +name = "dump_config" +source = "finecode_extension_runner.action_handlers.DumpConfigHandler" +env = "dev_workspace" + + +[[tool.finecode.action.dump_config.handlers]] +name = "dump_config_save" +source = "finecode_extension_runner.action_handlers.DumpConfigSaveHandler" +env = "dev_workspace" + [tool.finecode.action.install_deps_in_env] source = "finecode_extension_api.actions.install_deps_in_env.InstallDepsInEnvAction" -handlers = [ - { name = "install_deps_with_pip", source = "fine_python_pip.PipInstallDepsInEnvHandler", env = "dev_workspace", dependencies = [ - "fine_python_pip @ file:///home/user/Development/FineCode/finecode/extensions/fine_python_pip", - ] }, -] -config = {} + +[[tool.finecode.action.install_deps_in_env.handlers]] +name = "install_deps_with_pip" +source = "fine_python_pip.PipInstallDepsInEnvHandler" +env = "dev_workspace" +dependencies = ["fine_python_pip==0.1.2"] diff --git a/src/finecode/cli.py b/src/finecode/cli.py index db1c2b2..05fbbfa 100644 --- a/src/finecode/cli.py +++ b/src/finecode/cli.py @@ -81,7 +81,8 @@ def start_api( async def show_user_message( message: str, message_type: str ) -> None: - logger.log(message_type, message) + # user messages in CLI are not needed because CLI outputs own messages + ... @cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True)) @@ -194,6 +195,7 @@ def prepare_envs(trace: bool, logger.info(e) logger_utils.init_logger(trace=trace, stdout=True) + user_messages._notification_sender = show_user_message try: asyncio.run(prepare_envs_cmd.prepare_envs(workdir_path=pathlib.Path(os.getcwd()), recreate=recreate)) @@ -229,6 +231,7 @@ def dump_config( return logger_utils.init_logger(trace=trace, stdout=True) + user_messages._notification_sender = show_user_message try: asyncio.run(dump_config_cmd.dump_config(workdir_path=pathlib.Path(os.getcwd()), project_name=project)) diff --git a/src/finecode/cli_app/prepare_envs.py b/src/finecode/cli_app/prepare_envs.py index 89d577e..233ce49 100644 --- a/src/finecode/cli_app/prepare_envs.py +++ b/src/finecode/cli_app/prepare_envs.py @@ -48,7 +48,7 @@ async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: if recreate: remove_dev_workspace_envs(projects=projects, workdir_path=workdir_path) - await check_or_recreate_all_dev_workspace_envs(projects=projects, workdir_path=workdir_path, ws_context=ws_context) + await check_or_recreate_all_dev_workspace_envs(projects=projects, workdir_path=workdir_path, recreate=recreate, ws_context=ws_context) # now all 'dev_workspace' envs are valid, run 'prepare_runners' in them to create # venvs and install runners and presets in them. This is required to be able to @@ -72,7 +72,7 @@ async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: if result_return_code != 0: raise PrepareEnvsFailed(result_output) - + # reread projects configs, now with resolved presets # to be able to resolve presets, start dev_no_runtime runners first try: @@ -116,7 +116,7 @@ def remove_dev_workspace_envs(projects: list[domain.Project], workdir_path: path runner_manager.remove_runner_venv(runner_dir=project.dir_path, env_name='dev_workspace') -async def check_or_recreate_all_dev_workspace_envs(projects: list[domain.Project], workdir_path: pathlib.Path, ws_context: context.WorkspaceContext) -> None: +async def check_or_recreate_all_dev_workspace_envs(projects: list[domain.Project], workdir_path: pathlib.Path, recreate: bool, ws_context: context.WorkspaceContext) -> None: # NOTE: this function can start new extensions runner, don't forget to call # on_shutdown if you use it projects_dirs_with_valid_envs: list[pathlib.Path] = [] @@ -135,7 +135,10 @@ async def check_or_recreate_all_dev_workspace_envs(projects: list[domain.Project if runner_is_valid: projects_dirs_with_valid_envs.append(project.dir_path) else: - logger.warning(f"Runner for env 'dev_workspace' in project '{project.name}' is invalid, recreate it") + if recreate: + logger.trace(f"Recreate runner for env 'dev_workspace' in project '{project.name}'") + else: + logger.warning(f"Runner for env 'dev_workspace' in project '{project.name}' is invalid, recreate it") projects_dirs_with_invalid_envs.append(project.dir_path) # to recreate dev_workspace env, run `prepare_envs` in runner of current project diff --git a/src/finecode/cli_app/run.py b/src/finecode/cli_app/run.py index 8b26e33..afb06ae 100644 --- a/src/finecode/cli_app/run.py +++ b/src/finecode/cli_app/run.py @@ -17,8 +17,9 @@ def __init__(self, message: str) -> None: async def start_required_environments( - actions_by_projects: dict[pathlib.Path, list[str]], - ws_context: context.WorkspaceContext + actions_by_projects: dict[pathlib.Path, list[str]], + ws_context: context.WorkspaceContext, + update_config_in_running_runners: bool = False ) -> None: """Collect all required envs from actions that will be run and start them.""" required_envs_by_project: dict[pathlib.Path, set[str]] = {} @@ -56,6 +57,12 @@ async def start_required_environments( except Exception as e: logger.warning(f"Failed to start runner for env '{env_name}' in project '{project.name}': {e}") # TODO: raise error + else: + if update_config_in_running_runners: + runner = existing_runners[env_name] + logger.trace(f"Runner {runner.working_dir_path} {runner.env_name} is running already, update config") + # TODO: handle errors + await runner_manager.update_runner_config(runner=runner, project=project) async def run_actions( @@ -126,6 +133,15 @@ async def run_actions( else: projects = list(ws_context.ws_projects.values()) + # first read configs without presets to be able to start runners with presets + for project in projects: + try: + await read_configs.read_project_config(project=project, ws_context=ws_context, resolve_presets=False) + collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + except config_models.ConfigurationError as exception: + raise RunFailed(f"Reading project config and collecting actions in {project.dir_path} failed: {exception.message}") + + try: # 1. Start runners with presets to be able to resolve presets. Presets are # required to be able to collect all actions, actions handlers and configs. @@ -168,7 +184,7 @@ async def run_actions( # actions will be run in all projects inside actions_by_projects = find_projects_with_actions(ws_context, actions) - await start_required_environments(actions_by_projects, ws_context) + await start_required_environments(actions_by_projects, ws_context, update_config_in_running_runners=True) return await run_actions_in_all_projects( actions_by_projects, action_payload, ws_context, concurrently diff --git a/src/finecode/config/collect_actions.py b/src/finecode/config/collect_actions.py index acc14ad..24e6ea3 100644 --- a/src/finecode/config/collect_actions.py +++ b/src/finecode/config/collect_actions.py @@ -29,14 +29,28 @@ def collect_actions( actions = _collect_actions_in_config(config) project.actions = actions + action_handler_configs = _collect_action_handler_configs_in_config(config) + project.action_handler_configs = action_handler_configs + return actions +def _collect_action_handler_configs_in_config(config: dict[str, Any]) -> dict[str, dict[str, Any]]: + action_handlers_configs = config["tool"]["finecode"].get("action_handler", []) + action_handler_config_by_source: dict[str, dict[str, Any]] = {} + for handler_def in action_handlers_configs: + # TODO: validate that source field exist? + handler_config = handler_def.get('config', None) + if handler_config is not None: + action_handler_config_by_source[handler_def['source']] = handler_config + + return action_handler_config_by_source + + def _collect_actions_in_config( config: dict[str, Any], ) -> list[domain.Action]: actions: list[domain.Action] = [] - for action_name, action_def_raw in ( config["tool"]["finecode"].get("action", {}).items() ): @@ -52,10 +66,7 @@ def _collect_actions_in_config( domain.ActionHandler( name=handler.name, source=handler.source, - config=config["tool"]["finecode"] - .get("action_handler", {}) - .get(handler.name, {}) - .get("config", {}), + config=handler.config or {}, env=handler.env, dependencies=handler.dependencies ) diff --git a/src/finecode/config/config_models.py b/src/finecode/config/config_models.py index f90cee4..5c9b172 100644 --- a/src/finecode/config/config_models.py +++ b/src/finecode/config/config_models.py @@ -17,14 +17,6 @@ class FinecodeViewDefinition(BaseModel): source: str -class FinecodeConfig(BaseModel): - presets: list[FinecodePresetDefinition] = [] - actions: list[FinecodeActionDefinition] = [] - views: list[FinecodeViewDefinition] = [] - action: dict[str, dict[str, Any]] = {} - action_handler: dict[str, dict[str, Any]] = {} - - class PresetDefinition(BaseModel): extends: list[FinecodePresetDefinition] = [] @@ -33,11 +25,12 @@ class ActionHandlerDefinition(BaseModel): name: str source: str env: str - dependencies: list[str] + dependencies: list[str] = [] + config: dict[str, Any] | None = None class ActionDefinition(BaseModel): - source: str | None = None + source: str handlers: list[ActionHandlerDefinition] = [] config: dict[str, Any] | None = None diff --git a/src/finecode/config/read_configs.py b/src/finecode/config/read_configs.py index 85e312a..3beb02d 100644 --- a/src/finecode/config/read_configs.py +++ b/src/finecode/config/read_configs.py @@ -51,6 +51,7 @@ async def read_projects_in_dir( def_path=def_file, status=status, actions=actions, + env_configs={} ) ws_context.ws_projects[def_file.parent] = new_project new_projects.append(new_project) @@ -68,6 +69,29 @@ def get_dependency_name(dependency_str: str) -> str: return dependency_str +def read_env_configs(project_config: dict[str, Any]) -> dict[str, domain.EnvConfig]: + env_configs = {} + env_config_section = project_config.get('tool', {}).get('finecode', {}).get('env', {}) + for env_name, env_raw_config in env_config_section.items(): + if 'runner' in env_raw_config: + runner_raw_config = env_raw_config['runner'] + if 'debug' in runner_raw_config: + debug = runner_raw_config['debug'] + runner_config = domain.RunnerConfig(debug=debug) + env_config = domain.EnvConfig(runner_config=runner_config) + env_configs[env_name] = env_config + + # add default configs + deps_groups = project_config.get('dependency-groups', {}) + for group_name in deps_groups.keys(): + if group_name not in env_configs: + runner_config = domain.RunnerConfig(debug=False) + env_config = domain.EnvConfig(runner_config=runner_config) + env_configs[group_name] = env_config + + return env_configs + + async def read_project_config( project: domain.Project, ws_context: context.WorkspaceContext, resolve_presets: bool = True ) -> None: @@ -83,32 +107,54 @@ async def read_project_config( # TODO: cache instead of reading each time with open(base_config_path, 'r') as base_config_file: base_config = toml_loads(base_config_file.read()).value - _merge_projects_configs(base_config, base_config_path, project_def, project.def_path) - project_def = base_config + project_config = {} + _merge_projects_configs(project_config, project.def_path, base_config, base_config_path) finecode_raw_config = project_def.get("tool", {}).get("finecode", None) if finecode_raw_config and resolve_presets: - finecode_config = config_models.FinecodeConfig(**finecode_raw_config) + try: + presets = [config_models.FinecodePresetDefinition(**raw_preset) for raw_preset in finecode_raw_config.get('presets', [])] + except config_models.ValidationError as exception: + raise config_models.ConfigurationError(str(exception)) + # all presets expected to be in `dev_no_runtime` environment project_runners = ws_context.ws_projects_extension_runners[project.dir_path] # TODO: can it be the case that there is no such runner? dev_no_runtime_runner = project_runners['dev_no_runtime'] new_config = await collect_config_from_py_presets( - presets_sources=[preset.source for preset in finecode_config.presets], + presets_sources=[preset.source for preset in presets], def_path=project.def_path, runner=dev_no_runtime_runner, ) if new_config is not None: - _merge_projects_configs(project_def, project.def_path, new_config, project.def_path) + _merge_projects_configs(project_config, project.def_path, new_config, project.def_path) + + _merge_projects_configs(project_config, project.def_path, project_def, project.def_path) + # `_merge_projects_configs` merges only finecode config. Copy all other keys as + # is + for key, value in project_def.items(): + if key != 'tool': + project_config[key] = value + tool_raw_config = project_def.get('tool', None) + if tool_raw_config is not None: + if 'tool' not in project_config: + project_config['tool'] = {} + project_tool_config = project_config['tool'] + for key, value in tool_raw_config.items(): + if key != 'finecode': + project_tool_config[key] = value # add runtime dependency group if it's not explicitly declared - add_runtime_dependency_group_if_new(project_def) + add_runtime_dependency_group_if_new(project_config) - add_extension_runner_to_dependencies(project_def) + add_extension_runner_to_dependencies(project_config) - merge_handlers_dependencies_into_groups(project_def) + merge_handlers_dependencies_into_groups(project_config) - ws_context.ws_projects_raw_configs[project.dir_path] = project_def + ws_context.ws_projects_raw_configs[project.dir_path] = project_config + + env_configs = read_env_configs(project_config=project_config) + project.env_configs = env_configs else: logger.info( f"Project definition of type {project.def_path.name} is not supported yet" @@ -147,7 +193,19 @@ def read_preset_config( # preset_id is used only for logs to make them more useful logger.trace(f"Read preset config: {preset_id}") if not config_path.exists(): - raise config_models.ConfigurationError(f"preset.toml not found in project '{preset_id}'") + # if package is installed in editable mode, we will get path to root directory + # of the package, not to source directory. In such case check both flat and + # src layouts of the package + config_dir_path = config_path.parent + flat_path_to_src = config_dir_path / preset_id + if flat_path_to_src.exists(): + config_path = flat_path_to_src / 'preset.toml' + else: + src_path_to_src = config_dir_path / 'src' / preset_id + if src_path_to_src.exists(): + config_path = src_path_to_src / 'preset.toml' + else: + raise config_models.ConfigurationError(f"preset.toml not found in project '{preset_id}'") with open(config_path, "rb") as preset_toml_file: preset_toml = toml_loads(preset_toml_file.read()).value @@ -205,6 +263,55 @@ async def collect_config_from_py_presets( return config +def _merge_object_array_by_key(existing_array: list[dict[str, Any]], new_array: list[dict[str, Any]], key_field: str) -> None: + """ + Merges object arrays by a specified key field. + + For each object in new_array: + - If an object with the same key_field value exists in existing_array, deep merge them + - If no object with that key_field value exists, append the new object + + Args: + existing_array: The array to merge into + new_array: The array to merge from + key_field: The field name to use as the merge key (e.g., 'name', 'source') + """ + # Create a lookup map for existing objects by the key field + existing_by_key = {} + for i, obj in enumerate(existing_array): + if isinstance(obj, dict) and key_field in obj: + existing_by_key[obj[key_field]] = i + + # Process each new object + for new_obj in new_array: + if not isinstance(new_obj, dict) or key_field not in new_obj: + # If the object doesn't have the key field, just append it + existing_array.append(new_obj) + continue + + obj_key = new_obj[key_field] + if obj_key in existing_by_key: + # Merge with existing object + existing_index = existing_by_key[obj_key] + existing_obj = existing_array[existing_index] + _deep_merge_dicts(existing_obj, new_obj) + else: + # Add new object + existing_array.append(new_obj) + + +def _deep_merge_dicts(target: dict[str, Any], source: dict[str, Any]) -> None: + """ + Deep merge source dict into target dict. + Arrays are replaced entirely (not merged), following TOML semantics. + """ + for key, value in source.items(): + if key in target and isinstance(target[key], dict) and isinstance(value, dict): + _deep_merge_dicts(target[key], value) + else: + target[key] = value + + def _merge_projects_configs(config1: dict[str, Any], config1_filepath: Path, config2: dict[str, Any], config2_filepath: Path) -> None: # merge config2 in config1 without overwriting if "tool" not in config1: @@ -216,7 +323,7 @@ def _merge_projects_configs(config1: dict[str, Any], config1_filepath: Path, con tool_finecode_config2 = config2.get("tool", {}).get("finecode", {}) for key, value in tool_finecode_config2.items(): - if key == "action" or key == "action_handler": + if key == "action": # first process actions explicitly to merge correct configs assert isinstance(value, dict) if key not in tool_finecode_config1: @@ -235,6 +342,31 @@ def _merge_projects_configs(config1: dict[str, Any], config1_filepath: Path, con "config" ] action_config.update(action_info["config"]) + + # Handle handlers array merge by name + if "handlers" in action_info: + if "handlers" not in tool_finecode_config1[key][action_name]: + tool_finecode_config1[key][action_name]["handlers"] = [] + + existing_handlers = tool_finecode_config1[key][action_name]["handlers"] + new_handlers = action_info["handlers"] + + # Merge handlers by name + _merge_object_array_by_key(existing_handlers, new_handlers, 'name') + elif key == "action_handler": + # Handle action_handler array merge by source + if key not in tool_finecode_config1: + tool_finecode_config1[key] = [] + + existing_action_handlers = tool_finecode_config1[key] + # Ensure value is a list + if isinstance(value, list): + new_action_handlers = value + # Merge action_handlers by source + _merge_object_array_by_key(existing_action_handlers, new_action_handlers, 'source') + else: + # If it's not a list, just set it directly (shouldn't happen with TOML arrays but be safe) + tool_finecode_config1[key] = value elif key == "env": if 'env' not in tool_finecode_config1: tool_finecode_config1['env'] = {} @@ -266,6 +398,14 @@ def _merge_projects_configs(config1: dict[str, Any], config1_filepath: Path, con env_config1_deps[dependency_name]['path'] = new_path if 'editable' in dependency: env_config1_deps[dependency_name]['editable'] = dependency['editable'] + if 'runner' in env_config2: + if 'runner' not in env_config1: + env_config1['runner'] = {} + env_config1_runner = env_config1['runner'] + env_config2_runner = env_config2['runner'] + + if 'debug' in env_config2_runner: + env_config1_runner['debug'] = env_config2_runner['debug'] elif key in config1: tool_finecode_config1[key].update(value) else: diff --git a/src/finecode/domain.py b/src/finecode/domain.py index 14e7ef9..7518260 100644 --- a/src/finecode/domain.py +++ b/src/finecode/domain.py @@ -42,7 +42,8 @@ def __init__( dir_path: Path, def_path: Path, status: ProjectStatus, - actions: list[Action] | None = None, + env_configs: dict[str, EnvConfig], + actions: list[Action] | None = None ) -> None: self.name = name self.dir_path = dir_path @@ -51,6 +52,12 @@ def __init__( # None means actions were not collected yet # if project.status is RUNNING, then actions are not None self.actions = actions + # config by handler source + self.action_handler_configs: dict[str, dict[str, typing.Any]] = {} + # config by env name + # it always contains configs for all environments, even if user hasn't provided + # one explicitly(=there is a default config) + self.env_configs: dict[str, EnvConfig] = env_configs def __str__(self) -> str: return ( @@ -81,6 +88,16 @@ class ProjectStatus(Enum): CONFIG_VALID = auto() +class RunnerConfig: + def __init__(self, debug: bool) -> None: + self.debug = debug + + +class EnvConfig: + def __init__(self, runner_config: RunnerConfig) -> None: + self.runner_config = runner_config + + RootActions = list[str] ActionsDict = dict[str, Action] AllActions = ActionsDict @@ -117,4 +134,6 @@ class PartialResult(typing.NamedTuple): "Action", "Project", "TextDocumentInfo", + "RunnerConfig", + "EnvConfig" ] diff --git a/src/finecode/runner/manager.py b/src/finecode/runner/manager.py index 6ab1e71..735218f 100644 --- a/src/finecode/runner/manager.py +++ b/src/finecode/runner/manager.py @@ -88,10 +88,14 @@ async def start_extension_runner( f"--project-path={runner_dir.as_posix()}", f"--env-name={env_name}" ] - # TODO: config parameter for debug and debug port - # if runner_dir == Path("/home/user/Development/FineCode/finecode"): - # process_args.append("--debug") - # process_args.append("--debug-port=5681") + env_config = ws_context.ws_projects[runner_dir].env_configs[env_name] + runner_config = env_config.runner_config + # TODO: also check whether lsp server is available, without it doesn't make sense + # to start with debugger + if runner_config.debug: + process_args.append("--debug") + # TODO: find free port and pass it + process_args.append("--debug-port=5681") process_args_str: str = " ".join(process_args) client = await create_lsp_client_io( @@ -100,6 +104,7 @@ async def start_extension_runner( runner_dir, ) runner_info_instance.client = client + # TODO: recognize started debugger and send command to lsp server async def on_exit(): logger.debug(f"Extension Runner {runner_info_instance.working_dir_path} exited") @@ -150,8 +155,7 @@ async def stop_extension_runner(runner: runner_info.ExtensionRunnerInfo) -> None try: await runner_client.shutdown(runner=runner) except Exception as e: - # TODO: handle - logger.error(f"Failed to shutdown {e}") + logger.error(f"Failed to shutdown: {e}") await runner_client.exit(runner) logger.debug("Sent exit to server") @@ -171,8 +175,10 @@ def stop_extension_runner_sync(runner: runner_info.ExtensionRunnerInfo) -> None: try: runner_client.shutdown_sync(runner=runner) except Exception as e: - # TODO: handle - logger.error(f"Failed to shutdown {e}") + # currently we get (almost?) always this error. TODO: Investigate why + # mute for now to make output less verbose + # logger.error(f"Failed to shutdown: {e}") + ... runner_client.exit_sync(runner) logger.debug("Sent exit to server") @@ -314,7 +320,7 @@ async def start_runner(project_def: domain.Project, env_name: str, ws_context: c except config_models.ConfigurationError as exception: raise RunnerFailedToStart(f"Found problem in configuration of {project_def.dir_path}: {exception.message}") - await _update_runner_config(runner=runner, project=project_def) + await update_runner_config(runner=runner, project=project_def) await _finish_runner_init(runner=runner, project=project_def, ws_context=ws_context) return runner @@ -335,7 +341,7 @@ async def _init_runner( await _init_lsp_client(runner=runner, project=project) - await _update_runner_config(runner=runner, project=project) + await update_runner_config(runner=runner, project=project) await _finish_runner_init(runner=runner, project=project, ws_context=ws_context) @@ -368,10 +374,11 @@ async def _init_lsp_client(runner: runner_info.ExtensionRunnerInfo, project: dom logger.debug("LSP Client initialized") -async def _update_runner_config(runner: runner_info.ExtensionRunnerInfo, project: domain.Project) -> None: +async def update_runner_config(runner: runner_info.ExtensionRunnerInfo, project: domain.Project) -> None: assert project.actions is not None + config = runner_client.RunnerConfig(actions=project.actions, action_handler_configs=project.action_handler_configs) try: - await runner_client.update_config(runner, project.actions) + await runner_client.update_config(runner, config) except runner_client.BaseRunnerRequestException as exception: runner.status = runner_info.RunnerStatus.FAILED await notify_project_changed(project) diff --git a/src/finecode/runner/runner_client.py b/src/finecode/runner/runner_client.py index 931f833..7e6fdae 100644 --- a/src/finecode/runner/runner_client.py +++ b/src/finecode/runner/runner_client.py @@ -5,6 +5,7 @@ import asyncio import asyncio.subprocess +import dataclasses import enum import json import typing @@ -34,6 +35,9 @@ class ResponseTimeout(BaseRunnerRequestException): ... class ActionRunFailed(BaseRunnerRequestException): ... +class ActionRunStopped(BaseRunnerRequestException): ... + + async def log_process_log_streams(process: asyncio.subprocess.Process) -> None: stdout, stderr = await process.communicate() @@ -74,7 +78,7 @@ async def send_request( # await log_process_log_streams(process=runner.client._server) raise ResponseTimeout( f"Timeout {timeout}s for response on {method} to" - f" runner {runner.working_dir_path}" + f" runner {runner.working_dir_path} in env {runner.env_name}" ) except pygls_exceptions.JsonRpcInternalError as error: logger.error(f"JsonRpcInternalError: {error.message}") @@ -184,14 +188,22 @@ async def run_action( return_code = response.return_code raw_result = "" + stringified_result = response.result + # currently result is always dumped to json even if response format is expected to + # be a string. See docs of ER lsp server for more details. + raw_result = json.loads(stringified_result) if response.format == "string": - raw_result = response.result + result = raw_result elif response.format == "json" or response.format == "styled_text_json": - raw_result = json.loads(response.result) + # string was already converted to dict above + result = raw_result else: raise Exception(f"Not support result format: {response.format}") - return RunActionResponse(result=raw_result, return_code=return_code) + if response.status == 'stopped': + raise ActionRunStopped(message=result) + + return RunActionResponse(result=result, return_code=return_code) async def reload_action(runner: ExtensionRunnerInfo, action_name: str) -> None: @@ -228,9 +240,16 @@ async def resolve_package_path(runner: ExtensionRunnerInfo, package_name: str) - return {"packagePath": response.packagePath} +@dataclasses.dataclass +class RunnerConfig: + actions: list[domain.Action] + # config by handler source + action_handler_configs: dict[str, dict[str, Any]] + + async def update_config( runner: ExtensionRunnerInfo, - actions: list[domain.Action], + config: RunnerConfig ) -> None: await send_request( runner=runner, @@ -240,7 +259,7 @@ async def update_config( arguments=[ runner.working_dir_path.as_posix(), runner.working_dir_path.stem, - actions, + config, ], ), ) diff --git a/src/finecode/services.py b/src/finecode/services.py index 186ef6e..074af6d 100644 --- a/src/finecode/services.py +++ b/src/finecode/services.py @@ -161,7 +161,8 @@ async def _run_action_in_env_runner( options={"result_format": result_format}, ) except runner_client.BaseRunnerRequestException as error: - await user_messages.error(f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message}") - raise ActionRunFailed(f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message}") + runner_log_path = runner.working_dir_path / '.venvs' / runner.env_name / 'logs' / 'runner.log' + await user_messages.error(f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message} . Log file: {runner_log_path}") + raise ActionRunFailed(f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message} . Log file: {runner_log_path}") return response From 81dd3764ffa06926a3aa391549b52e1bc488414c Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Sun, 24 Aug 2025 08:42:55 +0200 Subject: [PATCH 11/46] Add missing dataclass decorators in handlers configs. Add setup.py in extensions to unify build process --- .../fine_python_black/__init__.py | 5 +- .../fine_python_black/action.py | 2 + extensions/fine_python_black/poetry.lock | 321 ------------------ extensions/fine_python_black/setup.py | 58 ++++ extensions/fine_python_flake8/setup.py | 58 ++++ .../fine_python_isort/action.py | 2 + extensions/fine_python_isort/setup.py | 58 ++++ .../prepare_envs_handler.py | 3 + .../prepare_runners_handler.py | 3 + 9 files changed, 185 insertions(+), 325 deletions(-) delete mode 100644 extensions/fine_python_black/poetry.lock create mode 100644 extensions/fine_python_black/setup.py create mode 100644 extensions/fine_python_flake8/setup.py create mode 100644 extensions/fine_python_isort/setup.py diff --git a/extensions/fine_python_black/fine_python_black/__init__.py b/extensions/fine_python_black/fine_python_black/__init__.py index 5c3b020..5ad85fe 100644 --- a/extensions/fine_python_black/fine_python_black/__init__.py +++ b/extensions/fine_python_black/fine_python_black/__init__.py @@ -1,7 +1,4 @@ -from .action import ( - BlackFormatHandler, - BlackFormatHandlerConfig, -) +from .action import BlackFormatHandler, BlackFormatHandlerConfig __all__ = [ "BlackFormatHandler", diff --git a/extensions/fine_python_black/fine_python_black/action.py b/extensions/fine_python_black/fine_python_black/action.py index 839c01c..f782132 100644 --- a/extensions/fine_python_black/fine_python_black/action.py +++ b/extensions/fine_python_black/fine_python_black/action.py @@ -1,5 +1,6 @@ from __future__ import annotations +import dataclasses import sys # import asyncio @@ -41,6 +42,7 @@ def get_black_mode(config: BlackFormatHandlerConfig) -> Mode: ) +@dataclasses.dataclass class BlackFormatHandlerConfig(code_action.ActionHandlerConfig): # TODO: should be set target_versions: list[ diff --git a/extensions/fine_python_black/poetry.lock b/extensions/fine_python_black/poetry.lock deleted file mode 100644 index b536707..0000000 --- a/extensions/fine_python_black/poetry.lock +++ /dev/null @@ -1,321 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "black" -version = "25.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "click" -version = "8.2.0" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "click-8.2.0-py3-none-any.whl", hash = "sha256:6b303f0b2aa85f1cb4e5303078fadcbcd4e476f114fab9b5007005711839325c"}, - {file = "click-8.2.0.tar.gz", hash = "sha256:f5452aeddd9988eefa20f90f05ab66f17fce1ee2a36907fd30b05bbb5953814d"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "platform_system == \"Windows\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pydantic" -version = "2.11.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, - {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.0" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, - {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "68cdb47eb2b61e9c00e52260c49119fd85326a2d471c39b4729430d7714ac637" diff --git a/extensions/fine_python_black/setup.py b/extensions/fine_python_black/setup.py new file mode 100644 index 0000000..30eee10 --- /dev/null +++ b/extensions/fine_python_black/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="fine_python_black", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/extensions/fine_python_flake8/setup.py b/extensions/fine_python_flake8/setup.py new file mode 100644 index 0000000..8e71890 --- /dev/null +++ b/extensions/fine_python_flake8/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="fine_python_flake8", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/extensions/fine_python_isort/fine_python_isort/action.py b/extensions/fine_python_isort/fine_python_isort/action.py index f3158b0..d20ec6d 100644 --- a/extensions/fine_python_isort/fine_python_isort/action.py +++ b/extensions/fine_python_isort/fine_python_isort/action.py @@ -1,5 +1,6 @@ from __future__ import annotations +import dataclasses from io import StringIO from pathlib import Path @@ -11,6 +12,7 @@ from finecode_extension_api.interfaces import icache, ilogger, iprocessexecutor +@dataclasses.dataclass class IsortFormatHandlerConfig(code_action.ActionHandlerConfig): profile: str = "" diff --git a/extensions/fine_python_isort/setup.py b/extensions/fine_python_isort/setup.py new file mode 100644 index 0000000..76ac3db --- /dev/null +++ b/extensions/fine_python_isort/setup.py @@ -0,0 +1,58 @@ +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info +import tempfile +import atexit +import shutil +import sys + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + +class CustomBuild(TempDirBuildMixin, build): + pass + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if '--editable' in sys.argv or '-e' in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + +setup( + name="fine_python_isort", + cmdclass={ + 'build': CustomBuild, + 'build_py': CustomBuildPy, + 'build_ext': CustomBuildExt, + 'egg_info': CustomEggInfo, + } +) \ No newline at end of file diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py index e5cf5de..f96882f 100644 --- a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py @@ -1,3 +1,5 @@ +import dataclasses + from finecode_extension_api.interfaces import ilogger, ifilemanager import virtualenv @@ -5,6 +7,7 @@ from finecode_extension_api.actions import prepare_envs as prepare_envs_action +@dataclasses.dataclass class VirtualenvPrepareEnvHandlerConfig(code_action.ActionHandlerConfig): ... diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py index c00f9c8..bc1fb7d 100644 --- a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py @@ -1,3 +1,5 @@ +import dataclasses + from finecode_extension_api.interfaces import ilogger, ifilemanager import virtualenv @@ -5,6 +7,7 @@ from finecode_extension_api.actions import prepare_runners as prepare_runners_action +@dataclasses.dataclass class VirtualenvPrepareRunnersHandlerConfig(code_action.ActionHandlerConfig): ... From 51ce658847defb5309a034646997c2498bb26416 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Sun, 24 Aug 2025 09:33:19 +0200 Subject: [PATCH 12/46] Fix default value of target_versions parameter in black config --- extensions/fine_python_black/fine_python_black/action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/fine_python_black/fine_python_black/action.py b/extensions/fine_python_black/fine_python_black/action.py index f782132..7899e63 100644 --- a/extensions/fine_python_black/fine_python_black/action.py +++ b/extensions/fine_python_black/fine_python_black/action.py @@ -50,7 +50,7 @@ class BlackFormatHandlerConfig(code_action.ActionHandlerConfig): # Literal["PY33", "PY34", "PY35", "PY36", "PY37", # "PY38", "PY39", "PY310", "PY311", "PY312"] str - ] = [] + ] = dataclasses.field(default_factory=list) # default black line length is 88: # https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-length line_length: int = 88 From 0265e0cd1fca3f63b99abce41c00d0a0286d23d3 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Sun, 24 Aug 2025 09:44:34 +0200 Subject: [PATCH 13/46] Formatting works again. Format the whole codebase --- extensions/fine_python_ast/setup.py | 33 ++- extensions/fine_python_black/setup.py | 33 ++- extensions/fine_python_flake8/setup.py | 33 ++- extensions/fine_python_isort/setup.py | 33 ++- .../fine_python_module_exports/setup.py | 33 ++- .../fine_python_mypy/action.py | 2 +- extensions/fine_python_mypy/setup.py | 67 +++++ extensions/fine_python_pip/setup.py | 33 ++- .../src/fine_python_pip/__init__.py | 7 +- .../install_deps_in_env_handler.py | 60 ++-- extensions/fine_python_virtualenv/setup.py | 33 ++- .../src/fine_python_virtualenv/__init__.py | 6 +- .../prepare_envs_handler.py | 20 +- .../prepare_runners_handler.py | 21 +- finecode_extension_api/setup.py | 33 ++- .../actions/dump_config.py | 2 +- .../finecode_extension_api/actions/format.py | 1 - .../actions/install_deps_in_env.py | 2 +- .../actions/prepare_envs.py | 14 +- .../actions/prepare_runners.py | 14 +- .../src/finecode_extension_api/code_action.py | 3 +- .../interfaces/iactionrunner.py | 6 +- .../interfaces/ifilemanager.py | 10 +- finecode_extension_runner/setup.py | 33 ++- .../src/finecode_extension_runner/__main__.py | 2 +- .../_services/run_action.py | 40 +-- .../action_handlers/__init__.py | 22 +- .../dependency_config_utils.py | 44 +-- .../action_handlers/dump_config.py | 20 +- .../action_handlers/dump_config_save.py | 12 +- .../prepare_envs_dump_configs.py | 66 +++-- .../prepare_envs_install_deps.py | 91 ++++-- .../prepare_envs_read_configs.py | 53 ++-- .../prepare_runners_dump_configs.py | 65 +++-- ...pare_runners_install_runner_and_presets.py | 131 ++++++--- .../prepare_runners_read_configs.py | 54 ++-- .../src/finecode_extension_runner/cli.py | 10 +- .../src/finecode_extension_runner/context.py | 2 +- .../finecode_extension_runner/di/_state.py | 1 - .../finecode_extension_runner/di/bootstrap.py | 49 ++-- .../finecode_extension_runner/di/resolver.py | 1 + .../src/finecode_extension_runner/domain.py | 4 +- .../impls/action_runner.py | 9 +- .../impls/file_manager.py | 6 +- .../impls/project_info_provider.py | 11 +- .../src/finecode_extension_runner/logs.py | 6 +- .../finecode_extension_runner/lsp_server.py | 39 +-- .../src/finecode_extension_runner/services.py | 19 +- .../src/finecode_extension_runner/start.py | 6 +- presets/fine_python_format/setup.py | 33 ++- presets/fine_python_lint/setup.py | 33 ++- presets/fine_python_recommended/setup.py | 33 ++- setup.py | 33 ++- src/finecode/cli.py | 40 +-- src/finecode/cli_app/dump_config.py | 20 +- src/finecode/cli_app/prepare_envs.py | 173 ++++++++---- src/finecode/cli_app/run.py | 115 +++++--- src/finecode/config/collect_actions.py | 10 +- src/finecode/config/dump_configs.py | 6 - src/finecode/config/read_configs.py | 264 +++++++++++------- src/finecode/domain.py | 19 +- src/finecode/logger_utils.py | 2 +- .../lsp_server/endpoints/action_tree.py | 16 +- .../lsp_server/endpoints/diagnostics.py | 28 +- .../lsp_server/endpoints/document_sync.py | 4 +- .../lsp_server/endpoints/formatting.py | 3 +- .../lsp_server/endpoints/inlay_hints.py | 3 +- src/finecode/lsp_server/lsp_server.py | 28 +- src/finecode/payload_preprocessor.py | 32 ++- src/finecode/project_analyzer.py | 28 +- src/finecode/proxy_utils.py | 24 +- src/finecode/runner/manager.py | 158 ++++++++--- src/finecode/runner/runner_client.py | 15 +- src/finecode/services.py | 81 +++--- 74 files changed, 1625 insertions(+), 871 deletions(-) create mode 100644 extensions/fine_python_mypy/setup.py delete mode 100644 src/finecode/config/dump_configs.py diff --git a/extensions/fine_python_ast/setup.py b/extensions/fine_python_ast/setup.py index dffa31f..50212ce 100644 --- a/extensions/fine_python_ast/setup.py +++ b/extensions/fine_python_ast/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_ast", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/extensions/fine_python_black/setup.py b/extensions/fine_python_black/setup.py index 30eee10..52a16dc 100644 --- a/extensions/fine_python_black/setup.py +++ b/extensions/fine_python_black/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_black", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/extensions/fine_python_flake8/setup.py b/extensions/fine_python_flake8/setup.py index 8e71890..d42fa6d 100644 --- a/extensions/fine_python_flake8/setup.py +++ b/extensions/fine_python_flake8/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_flake8", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/extensions/fine_python_isort/setup.py b/extensions/fine_python_isort/setup.py index 76ac3db..05cef8d 100644 --- a/extensions/fine_python_isort/setup.py +++ b/extensions/fine_python_isort/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_isort", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/extensions/fine_python_module_exports/setup.py b/extensions/fine_python_module_exports/setup.py index 6ce4b0e..ee123f8 100644 --- a/extensions/fine_python_module_exports/setup.py +++ b/extensions/fine_python_module_exports/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_module_exports", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/extensions/fine_python_mypy/fine_python_mypy/action.py b/extensions/fine_python_mypy/fine_python_mypy/action.py index 4a7265b..790241e 100644 --- a/extensions/fine_python_mypy/fine_python_mypy/action.py +++ b/extensions/fine_python_mypy/fine_python_mypy/action.py @@ -250,7 +250,7 @@ async def _run_dmypy(self, file_paths: list[Path], cwd: Path) -> str: "run", "--", *self.DMYPY_ARGS, - *file_paths_strs + *file_paths_strs, ] cmd = " ".join(cmd_parts) dmypy_run_process = await self.command_runner.run( diff --git a/extensions/fine_python_mypy/setup.py b/extensions/fine_python_mypy/setup.py new file mode 100644 index 0000000..1feff52 --- /dev/null +++ b/extensions/fine_python_mypy/setup.py @@ -0,0 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + +from setuptools import setup +from setuptools.command.build import build +from setuptools.command.build_ext import build_ext +from setuptools.command.build_py import build_py +from setuptools.command.egg_info import egg_info + +# Create a single temp directory for all build operations +_TEMP_BUILD_DIR = None + + +def get_temp_build_dir(pkg_name): + global _TEMP_BUILD_DIR + if _TEMP_BUILD_DIR is None: + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") + atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) + return _TEMP_BUILD_DIR + + +class TempDirBuildMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.build_base = temp_dir + + +class TempDirEggInfoMixin: + def initialize_options(self): + super().initialize_options() + temp_dir = get_temp_build_dir(self.distribution.get_name()) + self.egg_base = temp_dir + + +class CustomBuild(TempDirBuildMixin, build): + pass + + +class CustomBuildPy(TempDirBuildMixin, build_py): + pass + + +class CustomBuildExt(TempDirBuildMixin, build_ext): + pass + + +class CustomEggInfo(TempDirEggInfoMixin, egg_info): + def initialize_options(self): + # Don't use temp dir for editable installs + if "--editable" in sys.argv or "-e" in sys.argv: + egg_info.initialize_options(self) + else: + super().initialize_options() + + +setup( + name="fine_python_mypy", + cmdclass={ + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/extensions/fine_python_pip/setup.py b/extensions/fine_python_pip/setup.py index 8282045..a7659bf 100644 --- a/extensions/fine_python_pip/setup.py +++ b/extensions/fine_python_pip/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_pip", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/extensions/fine_python_pip/src/fine_python_pip/__init__.py b/extensions/fine_python_pip/src/fine_python_pip/__init__.py index bd2050f..8439702 100644 --- a/extensions/fine_python_pip/src/fine_python_pip/__init__.py +++ b/extensions/fine_python_pip/src/fine_python_pip/__init__.py @@ -1,8 +1,3 @@ -# TODO: is not used anymore -from .prepare_env_handler import PipPrepareEnvHandler from .install_deps_in_env_handler import PipInstallDepsInEnvHandler -__all__ = [ - 'PipPrepareEnvHandler', - 'PipInstallDepsInEnvHandler' -] \ No newline at end of file +__all__ = ["PipInstallDepsInEnvHandler"] diff --git a/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py b/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py index 1b80831..e1800d8 100644 --- a/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py +++ b/extensions/fine_python_pip/src/fine_python_pip/install_deps_in_env_handler.py @@ -3,8 +3,10 @@ import pathlib from finecode_extension_api import code_action -from finecode_extension_api.actions import install_deps_in_env as install_deps_in_env_action -from finecode_extension_api.interfaces import (icommandrunner, ilogger) +from finecode_extension_api.actions import ( + install_deps_in_env as install_deps_in_env_action, +) +from finecode_extension_api.interfaces import icommandrunner, ilogger @dataclasses.dataclass @@ -13,9 +15,17 @@ class PipInstallDepsInEnvHandlerConfig(code_action.ActionHandlerConfig): class PipInstallDepsInEnvHandler( - code_action.ActionHandler[install_deps_in_env_action.InstallDepsInEnvAction, PipInstallDepsInEnvHandlerConfig] + code_action.ActionHandler[ + install_deps_in_env_action.InstallDepsInEnvAction, + PipInstallDepsInEnvHandlerConfig, + ] ): - def __init__(self, config: PipInstallDepsInEnvHandlerConfig, command_runner: icommandrunner.ICommandRunner, logger: ilogger.ILogger) -> None: + def __init__( + self, + config: PipInstallDepsInEnvHandlerConfig, + command_runner: icommandrunner.ICommandRunner, + logger: ilogger.ILogger, + ) -> None: self.config = config self.command_runner = command_runner self.logger = logger @@ -29,10 +39,14 @@ async def run( dependencies = payload.dependencies venv_dir_path = payload.venv_dir_path project_dir_path = payload.project_dir_path - python_executable = venv_dir_path / 'bin' / 'python' + python_executable = venv_dir_path / "bin" / "python" - cmd = self._construct_pip_install_cmd(python_executable=python_executable, dependencies=dependencies) - error = await self._run_pip_cmd(cmd=cmd, env_name=env_name, project_dir_path=project_dir_path) + cmd = self._construct_pip_install_cmd( + python_executable=python_executable, dependencies=dependencies + ) + error = await self._run_pip_cmd( + cmd=cmd, env_name=env_name, project_dir_path=project_dir_path + ) if error is not None: errors = [error] else: @@ -40,8 +54,12 @@ async def run( return install_deps_in_env_action.InstallDepsInEnvRunResult(errors=errors) - def _construct_pip_install_cmd(self, python_executable: pathlib.Path, dependencies: list[install_deps_in_env_action.Dependency]) -> str: - install_params: str = '' + def _construct_pip_install_cmd( + self, + python_executable: pathlib.Path, + dependencies: list[install_deps_in_env_action.Dependency], + ) -> str: + install_params: str = "" if self.config.find_links is not None: for link in self.config.find_links: @@ -49,36 +67,40 @@ def _construct_pip_install_cmd(self, python_executable: pathlib.Path, dependenci for dependency in dependencies: if dependency.editable: - install_params += '-e ' + install_params += "-e " - if '@ file://' in dependency.version_or_source: + if "@ file://" in dependency.version_or_source: # dependency is specified as ' @ file://' but pip CLI supports # only 'file://' - start_idx_of_file_uri = dependency.version_or_source.index('file://') + start_idx_of_file_uri = dependency.version_or_source.index("file://") # put in single quoutes to avoid problems in case of spaces in path # because in CLI commands single dependencies are splitted by space - install_params += f"'{dependency.version_or_source[start_idx_of_file_uri:]}' " + install_params += ( + f"'{dependency.version_or_source[start_idx_of_file_uri:]}' " + ) else: # put in single quoutes to avoid problems in case of spaces in version, # because in CLI commands single dependencies are splitted by space install_params += f"'{dependency.name}{dependency.version_or_source}' " - cmd = f'{python_executable} -m pip --disable-pip-version-check install {install_params}' + cmd = f"{python_executable} -m pip --disable-pip-version-check install {install_params}" return cmd - async def _run_pip_cmd(self, cmd: str, env_name: str, project_dir_path: pathlib.Path) -> str | None: + async def _run_pip_cmd( + self, cmd: str, env_name: str, project_dir_path: pathlib.Path + ) -> str | None: process = await self.command_runner.run(cmd, cwd=project_dir_path) await process.wait_for_end() if process.get_exit_code() != 0: process_stdout = process.get_output() process_stderr = process.get_error_output() - logs = '' + logs = "" if len(process_stdout) > 0 and len(process_stderr) > 0: - logs = f'stdout: {process_stdout}\nstderr: {process_stderr}' + logs = f"stdout: {process_stdout}\nstderr: {process_stderr}" elif len(process_stdout) > 0: logs = process_stdout else: logs = process_stderr return f'Installation of dependencies "{cmd}" in env {env_name} from {project_dir_path} failed:\n{logs}' - - return None \ No newline at end of file + + return None diff --git a/extensions/fine_python_virtualenv/setup.py b/extensions/fine_python_virtualenv/setup.py index af096fe..c8b9eda 100644 --- a/extensions/fine_python_virtualenv/setup.py +++ b/extensions/fine_python_virtualenv/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_virtualenv", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py index cec0c11..bd3eaab 100644 --- a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/__init__.py @@ -1,8 +1,4 @@ from .prepare_envs_handler import VirtualenvPrepareEnvHandler from .prepare_runners_handler import VirtualenvPrepareRunnersHandler - -__all__ = [ - 'VirtualenvPrepareEnvHandler', - 'VirtualenvPrepareRunnersHandler' -] +__all__ = ["VirtualenvPrepareEnvHandler", "VirtualenvPrepareRunnersHandler"] diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py index f96882f..a732bec 100644 --- a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_envs_handler.py @@ -1,25 +1,26 @@ import dataclasses -from finecode_extension_api.interfaces import ilogger, ifilemanager import virtualenv from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_envs as prepare_envs_action +from finecode_extension_api.interfaces import ifilemanager, ilogger @dataclasses.dataclass -class VirtualenvPrepareEnvHandlerConfig(code_action.ActionHandlerConfig): - ... +class VirtualenvPrepareEnvHandlerConfig(code_action.ActionHandlerConfig): ... class VirtualenvPrepareEnvHandler( - code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, VirtualenvPrepareEnvHandlerConfig] + code_action.ActionHandler[ + prepare_envs_action.PrepareEnvsAction, VirtualenvPrepareEnvHandlerConfig + ] ): def __init__( self, config: VirtualenvPrepareEnvHandlerConfig, logger: ilogger.ILogger, - file_manager: ifilemanager.IFileManager + file_manager: ifilemanager.IFileManager, ) -> None: self.config = config self.logger = logger @@ -31,7 +32,7 @@ async def run( run_context: prepare_envs_action.PrepareEnvsRunContext, ) -> prepare_envs_action.PrepareEnvsRunResult: # create virtual envs - + # would it be faster parallel? for env_info in payload.envs: if payload.recreate and env_info.venv_dir_path.exists(): @@ -41,7 +42,12 @@ async def run( self.logger.info(f"Creating virtualenv {env_info.venv_dir_path}") if not env_info.venv_dir_path.exists(): # TODO: '-p ' - virtualenv.cli_run([env_info.venv_dir_path.as_posix()], options=None, setup_logging=False, env=None) + virtualenv.cli_run( + [env_info.venv_dir_path.as_posix()], + options=None, + setup_logging=False, + env=None, + ) else: self.logger.info(f"Virtualenv in {env_info} exists already") diff --git a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py index bc1fb7d..c546e91 100644 --- a/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py +++ b/extensions/fine_python_virtualenv/src/fine_python_virtualenv/prepare_runners_handler.py @@ -1,25 +1,27 @@ import dataclasses -from finecode_extension_api.interfaces import ilogger, ifilemanager import virtualenv from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_runners as prepare_runners_action +from finecode_extension_api.interfaces import ifilemanager, ilogger @dataclasses.dataclass -class VirtualenvPrepareRunnersHandlerConfig(code_action.ActionHandlerConfig): - ... +class VirtualenvPrepareRunnersHandlerConfig(code_action.ActionHandlerConfig): ... class VirtualenvPrepareRunnersHandler( - code_action.ActionHandler[prepare_runners_action.PrepareRunnersAction, VirtualenvPrepareRunnersHandlerConfig] + code_action.ActionHandler[ + prepare_runners_action.PrepareRunnersAction, + VirtualenvPrepareRunnersHandlerConfig, + ] ): def __init__( self, config: VirtualenvPrepareRunnersHandlerConfig, logger: ilogger.ILogger, - file_manager: ifilemanager.IFileManager + file_manager: ifilemanager.IFileManager, ) -> None: self.config = config self.logger = logger @@ -31,7 +33,7 @@ async def run( run_context: prepare_runners_action.PrepareRunnersRunContext, ) -> prepare_runners_action.PrepareRunnersRunResult: # create virtual envs - + # would it be faster parallel? for env_info in payload.envs: if payload.recreate and env_info.venv_dir_path.exists(): @@ -41,7 +43,12 @@ async def run( self.logger.info(f"Creating virtualenv {env_info.venv_dir_path}") if not env_info.venv_dir_path.exists(): # TODO: '-p ' - virtualenv.cli_run([env_info.venv_dir_path.as_posix()], options=None, setup_logging=False, env=None) + virtualenv.cli_run( + [env_info.venv_dir_path.as_posix()], + options=None, + setup_logging=False, + env=None, + ) else: self.logger.info(f"Virtualenv in {env_info} exists already") diff --git a/finecode_extension_api/setup.py b/finecode_extension_api/setup.py index c7e4537..195afdf 100644 --- a/finecode_extension_api/setup.py +++ b/finecode_extension_api/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="finecode_extension_api", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py b/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py index bfd70f8..6fb8b1c 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py @@ -46,7 +46,7 @@ def update(self, other: code_action.RunActionResult) -> None: self.config_dump = other.config_dump def to_text(self) -> str | textstyler.StyledText: - return '' + return "" class DumpConfigAction(code_action.Action): diff --git a/finecode_extension_api/src/finecode_extension_api/actions/format.py b/finecode_extension_api/src/finecode_extension_api/actions/format.py index e227980..fa251f0 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/format.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/format.py @@ -24,7 +24,6 @@ class FileInfo(NamedTuple): file_version: str - class FormatRunContext(code_action.RunActionContext): def __init__( self, diff --git a/finecode_extension_api/src/finecode_extension_api/actions/install_deps_in_env.py b/finecode_extension_api/src/finecode_extension_api/actions/install_deps_in_env.py index 151f56a..e8de2f8 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/install_deps_in_env.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/install_deps_in_env.py @@ -44,7 +44,7 @@ def update(self, other: code_action.RunActionResult) -> None: self.errors += other.errors def to_text(self) -> str | textstyler.StyledText: - return '\n'.join(self.errors) + return "\n".join(self.errors) @property def return_code(self) -> code_action.RunReturnCode: diff --git a/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py b/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py index 15ebe60..ff7b7e2 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/prepare_envs.py @@ -34,7 +34,7 @@ def __init__( run_id: int, ) -> None: super().__init__(run_id=run_id) - + # project def pathes are stored also in context, because prepare envs can run # tools like pip which expected 'normalized' project definition(=without # additional features which finecode provides). So the usual workflow looks like @@ -46,11 +46,15 @@ def __init__( # for example additional dependencies should be installed by adding handler # which inserts them into project definition instead of modying `install_deps` # handler - self.project_def_by_venv_dir_path: dict[pathlib.Path, dict[str, typing.Any]] = {} - + self.project_def_by_venv_dir_path: dict[pathlib.Path, dict[str, typing.Any]] = ( + {} + ) + async def init(self, initial_payload: PrepareEnvsRunPayload) -> None: for env_info in initial_payload.envs: - self.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = env_info.project_def_path + self.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = ( + env_info.project_def_path + ) @dataclasses.dataclass @@ -65,7 +69,7 @@ def update(self, other: code_action.RunActionResult) -> None: self.errors += other.errors def to_text(self) -> str | textstyler.StyledText: - return '\n'.join(self.errors) + return "\n".join(self.errors) @property def return_code(self) -> code_action.RunReturnCode: diff --git a/finecode_extension_api/src/finecode_extension_api/actions/prepare_runners.py b/finecode_extension_api/src/finecode_extension_api/actions/prepare_runners.py index 154c802..ec1a9fc 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/prepare_runners.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/prepare_runners.py @@ -34,7 +34,7 @@ def __init__( run_id: int, ) -> None: super().__init__(run_id=run_id) - + # project def pathes are stored also in context, because prepare envs can run # tools like pip which expected 'normalized' project definition(=without # additional features which finecode provides). So the usual workflow looks like @@ -46,11 +46,15 @@ def __init__( # for example additional dependencies should be installed by adding handler # which inserts them into project definition instead of modying `install_deps` # handler - self.project_def_by_venv_dir_path: dict[pathlib.Path, dict[str, typing.Any]] = {} - + self.project_def_by_venv_dir_path: dict[pathlib.Path, dict[str, typing.Any]] = ( + {} + ) + async def init(self, initial_payload: PrepareRunnersRunPayload) -> None: for env_info in initial_payload.envs: - self.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = env_info.project_def_path + self.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = ( + env_info.project_def_path + ) @dataclasses.dataclass @@ -65,7 +69,7 @@ def update(self, other: code_action.RunActionResult) -> None: self.errors += other.errors def to_text(self) -> str | textstyler.StyledText: - return '\n'.join(self.errors) + return "\n".join(self.errors) @property def return_code(self) -> code_action.RunReturnCode: diff --git a/finecode_extension_api/src/finecode_extension_api/code_action.py b/finecode_extension_api/src/finecode_extension_api/code_action.py index 55c4d45..a88fb28 100644 --- a/finecode_extension_api/src/finecode_extension_api/code_action.py +++ b/finecode_extension_api/src/finecode_extension_api/code_action.py @@ -4,10 +4,9 @@ import collections.abc import dataclasses import enum +import typing from pathlib import Path from typing import Generic, Protocol, TypeVar -import typing - from finecode_extension_api import partialresultscheduler, textstyler diff --git a/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py b/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py index 7b896b8..da6ef5b 100644 --- a/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py +++ b/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py @@ -13,12 +13,10 @@ def __init__(self, message: str) -> None: self.message = message -class ActionNotFound(BaseRunActionException): - ... +class ActionNotFound(BaseRunActionException): ... -class InvalidActionRunPayload(BaseRunActionException): - ... +class InvalidActionRunPayload(BaseRunActionException): ... class ActionRunFailed(BaseRunActionException): diff --git a/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py b/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py index 2f8cdc4..9b21f6a 100644 --- a/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py +++ b/finecode_extension_api/src/finecode_extension_api/interfaces/ifilemanager.py @@ -11,8 +11,8 @@ async def get_file_version(self, file_path: Path) -> str: async def save_file(self, file_path: Path, file_content) -> None: ... - async def create_dir(self, dir_path: Path, create_parents: bool = True, exist_ok: bool = True) -> None: - ... - - async def remove_dir(self, dir_path: Path) -> None: - ... \ No newline at end of file + async def create_dir( + self, dir_path: Path, create_parents: bool = True, exist_ok: bool = True + ) -> None: ... + + async def remove_dir(self, dir_path: Path) -> None: ... diff --git a/finecode_extension_runner/setup.py b/finecode_extension_runner/setup.py index c9c1924..ff9f46d 100644 --- a/finecode_extension_runner/setup.py +++ b/finecode_extension_runner/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="finecode_extension_runner", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/finecode_extension_runner/src/finecode_extension_runner/__main__.py b/finecode_extension_runner/src/finecode_extension_runner/__main__.py index 1e32b11..c55e863 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/__main__.py +++ b/finecode_extension_runner/src/finecode_extension_runner/__main__.py @@ -1,4 +1,4 @@ from finecode_extension_runner import cli if __name__ == "__main__": - cli.cli() \ No newline at end of file + cli.cli() diff --git a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py index 8252b11..144959e 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py +++ b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py @@ -1,22 +1,22 @@ import asyncio import collections.abc import dataclasses -import time import inspect +import time import typing from loguru import logger from pydantic.dataclasses import dataclass as pydantic_dataclass -from finecode_extension_runner import project_dirs, run_utils, schemas, global_state, domain, context from finecode_extension_api import code_action, textstyler from finecode_extension_api.interfaces import iactionrunner +from finecode_extension_runner import context, domain, global_state from finecode_extension_runner import ( partial_result_sender as partial_result_sender_module, ) +from finecode_extension_runner import project_dirs, run_utils, schemas from finecode_extension_runner.di import resolver as di_resolver - last_run_id: int = 0 partial_result_sender: partial_result_sender_module.PartialResultSender @@ -95,7 +95,7 @@ async def run_action( known_args={"run_id": lambda _: run_id}, params_to_ignore=["self"], ) - + run_context = action_exec_info.run_context_type(**constructor_args) # TODO: handler errors await run_context.init(initial_payload=payload) @@ -260,7 +260,7 @@ async def run_action( logger.trace( f"R{run_id} | Run action end '{request.action_name}', duration: {duration}ms" ) - + if not isinstance(action_result, code_action.RunActionResult): logger.error( f"R{run_id} | Unexpected result type: {type(action_result).__name__}" @@ -268,12 +268,17 @@ async def run_action( raise ActionFailedException( f"Unexpected result type: {type(action_result).__name__}" ) - - response = action_result_to_run_action_response(action_result, options.result_format) + + response = action_result_to_run_action_response( + action_result, options.result_format + ) return response -def action_result_to_run_action_response(action_result: code_action.RunActionResult, asked_result_format: typing.Literal['json'] | typing.Literal['string']) -> schemas.RunActionResponse: +def action_result_to_run_action_response( + action_result: code_action.RunActionResult, + asked_result_format: typing.Literal["json"] | typing.Literal["string"], +) -> schemas.RunActionResponse: serialized_result: dict[str, typing.Any] | str | None = None result_format = "string" run_return_code = code_action.RunReturnCode.SUCCESS @@ -309,11 +314,12 @@ def create_action_exec_info(action: domain.Action) -> domain.ActionExecInfo: raise e if not issubclass(action_type_def, code_action.Action): - raise Exception("Action class expected to be a subclass of finecode_extension_api.code_action.Action") + raise Exception( + "Action class expected to be a subclass of finecode_extension_api.code_action.Action" + ) payload_type = action_type_def.PAYLOAD_TYPE run_context_type = action_type_def.RUN_CONTEXT_TYPE - # TODO: validate that classes and correct subclasses? @@ -323,7 +329,6 @@ def create_action_exec_info(action: domain.Action) -> domain.ActionExecInfo: return action_exec_info - def resolve_func_args_with_di( func: typing.Callable, known_args: dict[str, typing.Callable[[typing.Any], typing.Any]] | None = None, @@ -392,7 +397,9 @@ async def execute_action_handler( f"Import of action handler '{handler.name}' failed(Run {run_id}): {handler.source}" ) - handler_global_config = runner_context.project.action_handler_configs.get(handler.source, None) + handler_global_config = runner_context.project.action_handler_configs.get( + handler.source, None + ) handler_raw_config = {} # TODO: deep merge instead? if handler_global_config is not None: @@ -453,7 +460,7 @@ def get_process_executor(param_type): raise ActionFailedException( f"Initialisation of action handler '{handler.name}' failed(Run {run_id}): {e}" ) - + exec_info.status = domain.ActionHandlerExecInfoStatus.INITIALIZED def get_run_payload(param_type): @@ -481,9 +488,11 @@ def get_run_context(param_type): except Exception as exception: if isinstance(exception, code_action.StopActionRunWithResult): action_result = exception.result - response = action_result_to_run_action_response(action_result, 'string') + response = action_result_to_run_action_response(action_result, "string") raise StopWithResponse(response=response) - elif isinstance(exception, iactionrunner.BaseRunActionException) or isinstance(exception, code_action.ActionFailedException): + elif isinstance(exception, iactionrunner.BaseRunActionException) or isinstance( + exception, code_action.ActionFailedException + ): error_str = exception.message else: logger.error("Unhandled exception in action handler:") @@ -502,7 +511,6 @@ def get_run_context(param_type): return execution_result - async def run_subresult_coros_concurrently( coros: list[collections.abc.Coroutine], send_partial_results: bool, diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py index 9648fba..555da63 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py @@ -1,20 +1,22 @@ from .dump_config import DumpConfigHandler +from .dump_config_save import DumpConfigSaveHandler from .prepare_envs_dump_configs import PrepareEnvsDumpConfigsHandler from .prepare_envs_install_deps import PrepareEnvsInstallDepsHandler from .prepare_envs_read_configs import PrepareEnvsReadConfigsHandler from .prepare_runners_dump_configs import PrepareRunnersDumpConfigsHandler -from .prepare_runners_install_runner_and_presets import PrepareRunnersInstallRunnerAndPresetsHandler +from .prepare_runners_install_runner_and_presets import ( + PrepareRunnersInstallRunnerAndPresetsHandler, +) from .prepare_runners_read_configs import PrepareRunnersReadConfigsHandler -from .dump_config_save import DumpConfigSaveHandler __all__ = [ - 'DumpConfigHandler', - 'PrepareEnvsDumpConfigsHandler', - 'PrepareEnvsInstallDepsHandler', - 'PrepareEnvsReadConfigsHandler', + "DumpConfigHandler", + "PrepareEnvsDumpConfigsHandler", + "PrepareEnvsInstallDepsHandler", + "PrepareEnvsReadConfigsHandler", # not used - 'PrepareRunnersDumpConfigsHandler', - 'PrepareRunnersInstallRunnerAndPresetsHandler', - 'PrepareRunnersReadConfigsHandler', - 'DumpConfigSaveHandler', + "PrepareRunnersDumpConfigsHandler", + "PrepareRunnersInstallRunnerAndPresetsHandler", + "PrepareRunnersReadConfigsHandler", + "DumpConfigSaveHandler", ] diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py index 2aaf440..cd4ed23 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py @@ -2,21 +2,25 @@ import typing -def make_project_config_pip_compatible(project_raw_config: dict[str, typing.Any], config_file_path: pathlib.Path) -> None: +def make_project_config_pip_compatible( + project_raw_config: dict[str, typing.Any], config_file_path: pathlib.Path +) -> None: # TODO: what to do with included groups in dependency groups? Inherit config from its env? - finecode_config = project_raw_config.get('tool', {}).get('finecode', {}) + finecode_config = project_raw_config.get("tool", {}).get("finecode", {}) # apply changes to dependencies from env configuration to deps groups - for env_name, env_config in finecode_config.get('env', {}).items(): - if 'dependencies' not in env_config: + for env_name, env_config in finecode_config.get("env", {}).items(): + if "dependencies" not in env_config: continue - - env_deps_group = project_raw_config.get('dependency-groups', {}).get(env_name, []) - dependencies = env_config['dependencies'] + + env_deps_group = project_raw_config.get("dependency-groups", {}).get( + env_name, [] + ) + dependencies = env_config["dependencies"] for dep_name, dep_params in dependencies.items(): # handle 'path'. 'editable' cannot be handled here because dependency # specifier doesn't support it. It will read and processed by # `install_deps` action - if 'path' in dep_params: + if "path" in dep_params: # replace dependency version / source in dependency group to this path # # check all dependencies because it can be duplicated: e.g. as explicit @@ -29,12 +33,16 @@ def make_project_config_pip_compatible(project_raw_config: dict[str, typing.Any] if len(dep_indexes_in_group) == 0: continue - - resolved_path_to_dep = pathlib.Path(dep_params['path']) + + resolved_path_to_dep = pathlib.Path(dep_params["path"]) if not resolved_path_to_dep.is_absolute(): # resolve relative to project dir where project def file is - resolved_path_to_dep = config_file_path.parent / resolved_path_to_dep - new_dep_str_in_group = f"{dep_name} @ file://{resolved_path_to_dep.as_posix()}" + resolved_path_to_dep = ( + config_file_path.parent / resolved_path_to_dep + ) + new_dep_str_in_group = ( + f"{dep_name} @ file://{resolved_path_to_dep.as_posix()}" + ) for idx in dep_indexes_in_group: env_deps_group[idx] = new_dep_str_in_group @@ -51,13 +59,17 @@ def get_dependency_name(dependency_str: str) -> str: class FailedToGetDependencies(Exception): - def __init__(self, message:str) -> None: + def __init__(self, message: str) -> None: self.message = message def raw_dep_to_dep_dict(raw_dep: str, env_deps_config: dict) -> dict[str, str | bool]: name = get_dependency_name(raw_dep) - version_or_source = raw_dep[len(name):] - editable = env_deps_config.get(name, {}).get('editable', False) - dep_dict = {"name": name, "version_or_source": version_or_source, "editable": editable} + version_or_source = raw_dep[len(name) :] + editable = env_deps_config.get(name, {}).get("editable", False) + dep_dict = { + "name": name, + "version_or_source": version_or_source, + "editable": editable, + } return dep_dict diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py index 60758b8..263ee38 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config.py @@ -11,15 +11,23 @@ class DumpConfigHandlerConfig(code_action.ActionHandlerConfig): ... class DumpConfigHandler( - code_action.ActionHandler[dump_config_action.DumpConfigAction, DumpConfigHandlerConfig] + code_action.ActionHandler[ + dump_config_action.DumpConfigAction, DumpConfigHandlerConfig + ] ): async def run( - self, payload: dump_config_action.DumpConfigRunPayload, run_context: dump_config_action.DumpConfigRunContext + self, + payload: dump_config_action.DumpConfigRunPayload, + run_context: dump_config_action.DumpConfigRunContext, ) -> dump_config_action.DumpConfigRunResult: # presets are resolved, remove tool.finecode.presets key to avoid repeating # resolving if dump config is processed - finecode_config = run_context.raw_config_dump.get('tool', {}).get('finecode', {}) - if 'presets' in finecode_config: - del finecode_config['presets'] + finecode_config = run_context.raw_config_dump.get("tool", {}).get( + "finecode", {} + ) + if "presets" in finecode_config: + del finecode_config["presets"] - return dump_config_action.DumpConfigRunResult(config_dump=run_context.raw_config_dump) + return dump_config_action.DumpConfigRunResult( + config_dump=run_context.raw_config_dump + ) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py index 3805958..2252f39 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dump_config_save.py @@ -12,7 +12,9 @@ class DumpConfigSaveHandlerConfig(code_action.ActionHandlerConfig): ... class DumpConfigSaveHandler( - code_action.ActionHandler[dump_config_action.DumpConfigAction, DumpConfigSaveHandlerConfig] + code_action.ActionHandler[ + dump_config_action.DumpConfigAction, DumpConfigSaveHandlerConfig + ] ): def __init__( self, @@ -21,7 +23,9 @@ def __init__( self.file_manager = file_manager async def run( - self, payload: dump_config_action.DumpConfigRunPayload, run_context: dump_config_action.DumpConfigRunContext + self, + payload: dump_config_action.DumpConfigRunPayload, + run_context: dump_config_action.DumpConfigRunContext, ) -> dump_config_action.DumpConfigRunResult: raw_config_str = tomlkit.dumps(run_context.raw_config_dump) target_file_dir_path = payload.target_file_path.parent @@ -31,4 +35,6 @@ async def run( file_path=payload.target_file_path, file_content=raw_config_str ) - return dump_config_action.DumpConfigRunResult(config_dump=run_context.raw_config_dump) + return dump_config_action.DumpConfigRunResult( + config_dump=run_context.raw_config_dump + ) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py index 9347719..edab5fb 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py @@ -3,7 +3,11 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_envs as prepare_envs_action -from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger +from finecode_extension_api.interfaces import ( + iactionrunner, + ilogger, + iprojectinfoprovider, +) @dataclasses.dataclass @@ -11,38 +15,60 @@ class PrepareEnvsDumpConfigsHandlerConfig(code_action.ActionHandlerConfig): ... class PrepareEnvsDumpConfigsHandler( - code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, PrepareEnvsDumpConfigsHandlerConfig] + code_action.ActionHandler[ + prepare_envs_action.PrepareEnvsAction, PrepareEnvsDumpConfigsHandlerConfig + ] ): - def __init__(self, action_runner: iactionrunner.IActionRunner, project_info_provider: iprojectinfoprovider.IProjectInfoProvider, logger: ilogger.ILogger) -> None: + def __init__( + self, + action_runner: iactionrunner.IActionRunner, + project_info_provider: iprojectinfoprovider.IProjectInfoProvider, + logger: ilogger.ILogger, + ) -> None: self.action_runner = action_runner self.project_info_provider = project_info_provider self.logger = logger - + async def run( - self, payload: prepare_envs_action.PrepareEnvsRunPayload, run_context: prepare_envs_action.PrepareEnvsRunContext + self, + payload: prepare_envs_action.PrepareEnvsRunPayload, + run_context: prepare_envs_action.PrepareEnvsRunContext, ) -> prepare_envs_action.PrepareEnvsRunResult: - project_defs_pathes = set([env_info.project_def_path for env_info in payload.envs]) + project_defs_pathes = set( + [env_info.project_def_path for env_info in payload.envs] + ) if len(project_defs_pathes) != 1: - raise code_action.ActionFailedException("prepare_envs action currently supports only preparing environments in the same project where it is running(dump_configs handler)") - + raise code_action.ActionFailedException( + "prepare_envs action currently supports only preparing environments in the same project where it is running(dump_configs handler)" + ) + project_raw_config = await self.project_info_provider.get_project_raw_config() - + project_def_path = project_defs_pathes.pop() project_dir_path = project_def_path.parent # TODO: unify with call of dump_config in CLI - dump_dir_path = project_dir_path / 'finecode_config_dump' + dump_dir_path = project_dir_path / "finecode_config_dump" try: - dump_config_result = await self.action_runner.run_action(name='dump_config', payload={ - "source_file_path": project_def_path, - "project_raw_config": project_raw_config, - "target_file_path": dump_dir_path / 'pyproject.toml' - }) - new_project_def_path = dump_dir_path / 'pyproject.toml' + dump_config_result = await self.action_runner.run_action( + name="dump_config", + payload={ + "source_file_path": project_def_path, + "project_raw_config": project_raw_config, + "target_file_path": dump_dir_path / "pyproject.toml", + }, + ) + new_project_def_path = dump_dir_path / "pyproject.toml" for env_info in payload.envs: - run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = new_project_def_path - run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = dump_config_result['config_dump'] + run_context.project_def_path_by_venv_dir_path[ + env_info.venv_dir_path + ] = new_project_def_path + run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = ( + dump_config_result["config_dump"] + ) except iactionrunner.BaseRunActionException as exception: - raise code_action.ActionFailedException(f"Running 'dump_config' action as part of 'prepare_envs' failed: {type(exception)}, {exception.message}") + raise code_action.ActionFailedException( + f"Running 'dump_config' action as part of 'prepare_envs' failed: {type(exception)}, {exception.message}" + ) # after dumping config in another directory, pathes to project files like # readme and source files are wrong. We cannot just change the pathes to the new @@ -61,7 +87,7 @@ async def run( # # - dir with dumped config # # - dumped config # continue - + # new_item_path = dump_dir_path / item.name # if new_item_path.exists(): # if new_item_path.is_symlink(): diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py index a4c9536..8f87893 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_install_deps.py @@ -5,7 +5,11 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_envs as prepare_envs_action -from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger +from finecode_extension_api.interfaces import ( + iactionrunner, + ilogger, + iprojectinfoprovider, +) from finecode_extension_runner.action_handlers import dependency_config_utils @@ -14,14 +18,20 @@ class PrepareEnvsInstallDepsHandlerConfig(code_action.ActionHandlerConfig): ... class PrepareEnvsInstallDepsHandler( - code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, PrepareEnvsInstallDepsHandlerConfig] + code_action.ActionHandler[ + prepare_envs_action.PrepareEnvsAction, PrepareEnvsInstallDepsHandlerConfig + ] ): - def __init__(self, action_runner: iactionrunner.IActionRunner, logger: ilogger.ILogger) -> None: + def __init__( + self, action_runner: iactionrunner.IActionRunner, logger: ilogger.ILogger + ) -> None: self.action_runner = action_runner self.logger = logger - + async def run( - self, payload: prepare_envs_action.PrepareEnvsRunPayload, run_context: prepare_envs_action.PrepareEnvsRunContext + self, + payload: prepare_envs_action.PrepareEnvsRunPayload, + run_context: prepare_envs_action.PrepareEnvsRunContext, ) -> prepare_envs_action.PrepareEnvsRunResult: envs = payload.envs @@ -29,40 +39,69 @@ async def run( try: async with asyncio.TaskGroup() as tg: for env in envs: - project_def = run_context.project_def_by_venv_dir_path[env.venv_dir_path] - + project_def = run_context.project_def_by_venv_dir_path[ + env.venv_dir_path + ] + # straightforward solution for now - deps_groups = project_def.get('dependency-groups', {}) + deps_groups = project_def.get("dependency-groups", {}) env_raw_deps = deps_groups.get(env.name, []) - env_deps_config = project_def.get('tool', {}).get('finecode', {}).get('env', {}).get(env.name, {}).get('dependencies', {}) + env_deps_config = ( + project_def.get("tool", {}) + .get("finecode", {}) + .get("env", {}) + .get(env.name, {}) + .get("dependencies", {}) + ) dependencies = [] - - process_raw_deps(env_raw_deps, env_deps_config, dependencies, deps_groups) - task = tg.create_task(self.action_runner.run_action(name='install_deps_in_env', payload={ - "env_name": env.name, - "venv_dir_path": env.venv_dir_path, - "project_dir_path": env.project_def_path.parent, - "dependencies": dependencies - })) + process_raw_deps( + env_raw_deps, env_deps_config, dependencies, deps_groups + ) + + task = tg.create_task( + self.action_runner.run_action( + name="install_deps_in_env", + payload={ + "env_name": env.name, + "venv_dir_path": env.venv_dir_path, + "project_dir_path": env.project_def_path.parent, + "dependencies": dependencies, + }, + ) + ) install_deps_tasks.append(task) except ExceptionGroup as eg: - error_str = '. '.join([str(exception) for exception in eg.exceptions]) + error_str = ". ".join([str(exception) for exception in eg.exceptions]) raise code_action.ActionFailedException(error_str) install_deps_results = [task.result() for task in install_deps_tasks] - errors: list[str] = list(itertools.chain.from_iterable([result['errors'] for result in install_deps_results])) + errors: list[str] = list( + itertools.chain.from_iterable( + [result["errors"] for result in install_deps_results] + ) + ) return prepare_envs_action.PrepareEnvsRunResult(errors=errors) -def process_raw_deps(raw_deps: list, env_deps_config, dependencies, deps_groups) -> None: +def process_raw_deps( + raw_deps: list, env_deps_config, dependencies, deps_groups +) -> None: for raw_dep in raw_deps: if isinstance(raw_dep, str): name = dependency_config_utils.get_dependency_name(raw_dep) - version_or_source = raw_dep[len(name):] - editable = env_deps_config.get(name, {}).get('editable', False) - dependencies.append({"name": name, "version_or_source": version_or_source, "editable": editable}) - elif isinstance(raw_dep, dict) and 'include-group' in raw_dep: - included_group_deps = deps_groups.get(raw_dep['include-group'], []) - process_raw_deps(included_group_deps, env_deps_config, dependencies, deps_groups) + version_or_source = raw_dep[len(name) :] + editable = env_deps_config.get(name, {}).get("editable", False) + dependencies.append( + { + "name": name, + "version_or_source": version_or_source, + "editable": editable, + } + ) + elif isinstance(raw_dep, dict) and "include-group" in raw_dep: + included_group_deps = deps_groups.get(raw_dep["include-group"], []) + process_raw_deps( + included_group_deps, env_deps_config, dependencies, deps_groups + ) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py index 3d78948..982d8ba 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py @@ -5,9 +5,11 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_envs as prepare_envs_action -from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger - - +from finecode_extension_api.interfaces import ( + iactionrunner, + ilogger, + iprojectinfoprovider, +) from finecode_extension_runner.action_handlers import dependency_config_utils @@ -16,29 +18,46 @@ class PrepareEnvsReadConfigsHandlerConfig(code_action.ActionHandlerConfig): ... class PrepareEnvsReadConfigsHandler( - code_action.ActionHandler[prepare_envs_action.PrepareEnvsAction, PrepareEnvsReadConfigsHandlerConfig] + code_action.ActionHandler[ + prepare_envs_action.PrepareEnvsAction, PrepareEnvsReadConfigsHandlerConfig + ] ): - def __init__(self, action_runner: iactionrunner.IActionRunner, project_info_provider: iprojectinfoprovider.IProjectInfoProvider, logger: ilogger.ILogger) -> None: + def __init__( + self, + action_runner: iactionrunner.IActionRunner, + project_info_provider: iprojectinfoprovider.IProjectInfoProvider, + logger: ilogger.ILogger, + ) -> None: self.action_runner = action_runner self.project_info_provider = project_info_provider self.logger = logger - + async def run( - self, payload: prepare_envs_action.PrepareEnvsRunPayload, run_context: prepare_envs_action.PrepareEnvsRunContext + self, + payload: prepare_envs_action.PrepareEnvsRunPayload, + run_context: prepare_envs_action.PrepareEnvsRunContext, ) -> prepare_envs_action.PrepareEnvsRunResult: - project_defs_pathes = set([env_info.project_def_path for env_info in payload.envs]) + project_defs_pathes = set( + [env_info.project_def_path for env_info in payload.envs] + ) if len(project_defs_pathes) != 1: - ... # TODO: error - + ... # TODO: error + project_raw_config = await self.project_info_provider.get_project_raw_config() - + project_def_path = project_defs_pathes.pop() project_dir_path = project_def_path.parent - - dependency_config_utils.make_project_config_pip_compatible(project_raw_config, project_def_path) - + + dependency_config_utils.make_project_config_pip_compatible( + project_raw_config, project_def_path + ) + for env_info in payload.envs: - run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = project_def_path - run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = project_raw_config - + run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = ( + project_def_path + ) + run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = ( + project_raw_config + ) + return prepare_envs_action.PrepareEnvsRunResult(errors=[]) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py index 350dbe8..725b065 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py @@ -3,7 +3,11 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_runners as prepare_runners_action -from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger +from finecode_extension_api.interfaces import ( + iactionrunner, + ilogger, + iprojectinfoprovider, +) @dataclasses.dataclass @@ -11,37 +15,60 @@ class PrepareRunnersDumpConfigsHandlerConfig(code_action.ActionHandlerConfig): . class PrepareRunnersDumpConfigsHandler( - code_action.ActionHandler[prepare_runners_action.PrepareRunnersAction, PrepareRunnersDumpConfigsHandlerConfig] + code_action.ActionHandler[ + prepare_runners_action.PrepareRunnersAction, + PrepareRunnersDumpConfigsHandlerConfig, + ] ): - def __init__(self, action_runner: iactionrunner.IActionRunner, project_info_provider: iprojectinfoprovider.IProjectInfoProvider, logger: ilogger.ILogger) -> None: + def __init__( + self, + action_runner: iactionrunner.IActionRunner, + project_info_provider: iprojectinfoprovider.IProjectInfoProvider, + logger: ilogger.ILogger, + ) -> None: self.action_runner = action_runner self.project_info_provider = project_info_provider self.logger = logger - + async def run( - self, payload: prepare_runners_action.PrepareRunnersRunPayload, run_context: prepare_runners_action.PrepareRunnersRunContext + self, + payload: prepare_runners_action.PrepareRunnersRunPayload, + run_context: prepare_runners_action.PrepareRunnersRunContext, ) -> prepare_runners_action.PrepareRunnersRunResult: - project_defs_pathes = set([env_info.project_def_path for env_info in payload.envs]) + project_defs_pathes = set( + [env_info.project_def_path for env_info in payload.envs] + ) if len(project_defs_pathes) != 1: - raise code_action.ActionFailedException("prepare_envs action currently supports only preparing environments in the same project where it is running(dump_configs handler)") - + raise code_action.ActionFailedException( + "prepare_envs action currently supports only preparing environments in the same project where it is running(dump_configs handler)" + ) + project_raw_config = await self.project_info_provider.get_project_raw_config() - + project_def_path = project_defs_pathes.pop() project_dir_path = project_def_path.parent # TODO: unify with call of dump_config in CLI - dump_dir_path = project_dir_path / 'finecode_config_dump' + dump_dir_path = project_dir_path / "finecode_config_dump" try: - dump_config_result = await self.action_runner.run_action(name='dump_config', payload={ - "source_file_path": project_def_path, - "project_raw_config": project_raw_config, - "target_file_path": dump_dir_path / 'pyproject.toml' - }) - new_project_def_path = dump_dir_path / 'pyproject.toml' + dump_config_result = await self.action_runner.run_action( + name="dump_config", + payload={ + "source_file_path": project_def_path, + "project_raw_config": project_raw_config, + "target_file_path": dump_dir_path / "pyproject.toml", + }, + ) + new_project_def_path = dump_dir_path / "pyproject.toml" for env_info in payload.envs: - run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = new_project_def_path - run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = dump_config_result['config_dump'] + run_context.project_def_path_by_venv_dir_path[ + env_info.venv_dir_path + ] = new_project_def_path + run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = ( + dump_config_result["config_dump"] + ) except iactionrunner.BaseRunActionException as exception: - raise code_action.ActionFailedException(f"Running 'dump_config' action as part of 'prepare_envs' failed: {type(exception)}, {exception.message}") + raise code_action.ActionFailedException( + f"Running 'dump_config' action as part of 'prepare_envs' failed: {type(exception)}, {exception.message}" + ) return prepare_runners_action.PrepareRunnersRunResult(errors=[]) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py index ed411a6..f16dbfe 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_install_runner_and_presets.py @@ -6,23 +6,36 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_runners as prepare_runners_action -from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger +from finecode_extension_api.interfaces import ( + iactionrunner, + ilogger, + iprojectinfoprovider, +) from finecode_extension_runner.action_handlers import dependency_config_utils @dataclasses.dataclass -class PrepareRunnersInstallRunnerAndPresetsHandlerConfig(code_action.ActionHandlerConfig): ... +class PrepareRunnersInstallRunnerAndPresetsHandlerConfig( + code_action.ActionHandlerConfig +): ... class PrepareRunnersInstallRunnerAndPresetsHandler( - code_action.ActionHandler[prepare_runners_action.PrepareRunnersAction, PrepareRunnersInstallRunnerAndPresetsHandlerConfig] + code_action.ActionHandler[ + prepare_runners_action.PrepareRunnersAction, + PrepareRunnersInstallRunnerAndPresetsHandlerConfig, + ] ): - def __init__(self, action_runner: iactionrunner.IActionRunner, logger: ilogger.ILogger) -> None: + def __init__( + self, action_runner: iactionrunner.IActionRunner, logger: ilogger.ILogger + ) -> None: self.action_runner = action_runner self.logger = logger - + async def run( - self, payload: prepare_runners_action.PrepareRunnersRunPayload, run_context: prepare_runners_action.PrepareRunnersRunContext + self, + payload: prepare_runners_action.PrepareRunnersRunPayload, + run_context: prepare_runners_action.PrepareRunnersRunContext, ) -> prepare_runners_action.PrepareRunnersRunResult: # find finecode_extension_runner in deps # find presets in config and their version in deps @@ -32,23 +45,34 @@ async def run( dependencies_by_env: dict[str, list[dict]] = {} for env in envs: project_def = run_context.project_def_by_venv_dir_path[env.venv_dir_path] - project_def_path = run_context.project_def_path_by_venv_dir_path[env.venv_dir_path] + project_def_path = run_context.project_def_path_by_venv_dir_path[ + env.venv_dir_path + ] try: - dependencies = get_dependencies_in_project_raw_config(project_def, env.name) + dependencies = get_dependencies_in_project_raw_config( + project_def, env.name + ) except FailedToGetDependencies as exception: - raise code_action.ActionFailedException(f"Failed to get dependencies of env {env.name} in {project_def_path} (install_runner_and_presets handler)") + raise code_action.ActionFailedException( + f"Failed to get dependencies of env {env.name} in {project_def_path} (install_runner_and_presets handler)" + ) dependencies_by_env[env.name] = dependencies install_deps_tasks: list[asyncio.Task] = [] try: async with asyncio.TaskGroup() as tg: for env in envs: - task = tg.create_task(self.action_runner.run_action(name='install_deps_in_env', payload={ - "env_name": env.name, - "venv_dir_path": env.venv_dir_path, - "project_dir_path": env.project_def_path.parent, - "dependencies": dependencies_by_env[env.name] - })) + task = tg.create_task( + self.action_runner.run_action( + name="install_deps_in_env", + payload={ + "env_name": env.name, + "venv_dir_path": env.venv_dir_path, + "project_dir_path": env.project_def_path.parent, + "dependencies": dependencies_by_env[env.name], + }, + ) + ) install_deps_tasks.append(task) except ExceptionGroup as eg: errors: list[str] = [] @@ -57,31 +81,41 @@ async def run( errors.append(exception.message) else: # unexpected exception - error_str = '. '.join([str(exception) for exception in eg.exceptions]) + error_str = ". ".join( + [str(exception) for exception in eg.exceptions] + ) raise code_action.ActionFailedException(error_str) - + result = prepare_runners_action.PrepareRunnersRunResult(errors=errors) raise code_action.StopActionRunWithResult(result=result) install_deps_results = [task.result() for task in install_deps_tasks] - errors: list[str] = list(itertools.chain.from_iterable([result['errors'] for result in install_deps_results])) + errors: list[str] = list( + itertools.chain.from_iterable( + [result["errors"] for result in install_deps_results] + ) + ) result = prepare_runners_action.PrepareRunnersRunResult(errors=errors) return result class FailedToGetDependencies(Exception): - def __init__(self, message:str) -> None: + def __init__(self, message: str) -> None: self.message = message -def get_dependencies_in_project_raw_config(project_raw_config: dict[str, typing.Any], env_name: str): +def get_dependencies_in_project_raw_config( + project_raw_config: dict[str, typing.Any], env_name: str +): # returns dependencies: presets and extension runner - presets_in_config = project_raw_config.get('tool', {}).get('finecode', {}).get('presets', []) + presets_in_config = ( + project_raw_config.get("tool", {}).get("finecode", {}).get("presets", []) + ) presets_packages_names: list[str] = [] for preset_def in presets_in_config: try: - preset_package = preset_def.get('source') + preset_package = preset_def.get("source") except KeyError: # workspace manager validates configuration and source should # always exist, but still handle @@ -89,29 +123,54 @@ def get_dependencies_in_project_raw_config(project_raw_config: dict[str, typing. presets_packages_names.append(preset_package) # straightforward solution for now - deps_groups = project_raw_config.get('dependency-groups', {}) + deps_groups = project_raw_config.get("dependency-groups", {}) env_raw_deps = deps_groups.get(env_name, []) - env_deps_config = project_raw_config.get('tool', {}).get('finecode', {}).get('env', {}).get(env_name, {}).get('dependencies', {}) + env_deps_config = ( + project_raw_config.get("tool", {}) + .get("finecode", {}) + .get("env", {}) + .get(env_name, {}) + .get("dependencies", {}) + ) dependencies = [] - + try: - runner_dep = next(dep for dep in env_raw_deps if isinstance(dep, str) and dependency_config_utils.get_dependency_name(dep) == 'finecode_extension_runner') + runner_dep = next( + dep + for dep in env_raw_deps + if isinstance(dep, str) + and dependency_config_utils.get_dependency_name(dep) + == "finecode_extension_runner" + ) except StopIteration: - raise FailedToGetDependencies(f"prepare_runners expects finecode_extension_runner dependency in each environment, but it was not found in {env_name}") - - runner_dep_dict = dependency_config_utils.raw_dep_to_dep_dict(raw_dep=runner_dep, env_deps_config=env_deps_config) + raise FailedToGetDependencies( + f"prepare_runners expects finecode_extension_runner dependency in each environment, but it was not found in {env_name}" + ) + + runner_dep_dict = dependency_config_utils.raw_dep_to_dep_dict( + raw_dep=runner_dep, env_deps_config=env_deps_config + ) dependencies.append(runner_dep_dict) - + for preset_package in presets_packages_names: try: - preset_dep = next(dep for dep in env_raw_deps if isinstance(dep, str) and dependency_config_utils.get_dependency_name(dep) == preset_package) + preset_dep = next( + dep + for dep in env_raw_deps + if isinstance(dep, str) + and dependency_config_utils.get_dependency_name(dep) == preset_package + ) except StopIteration: - if env_name == 'dev_no_runtime': + if env_name == "dev_no_runtime": # all preset packages must be in 'dev_no_runtime' env - raise FailedToGetDependencies(f"'{preset_package}' is used as preset source, but not declared in 'dev_no_runtime' dependency group") + raise FailedToGetDependencies( + f"'{preset_package}' is used as preset source, but not declared in 'dev_no_runtime' dependency group" + ) else: continue - - preset_dep_dict = dependency_config_utils.raw_dep_to_dep_dict(raw_dep=preset_dep, env_deps_config=env_deps_config) + + preset_dep_dict = dependency_config_utils.raw_dep_to_dep_dict( + raw_dep=preset_dep, env_deps_config=env_deps_config + ) dependencies.append(preset_dep_dict) - return dependencies \ No newline at end of file + return dependencies diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py index 85f66c3..db71a67 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py @@ -3,38 +3,60 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_runners as prepare_runners_action -from finecode_extension_api.interfaces import iactionrunner, iprojectinfoprovider, ilogger - +from finecode_extension_api.interfaces import ( + iactionrunner, + ilogger, + iprojectinfoprovider, +) from finecode_extension_runner.action_handlers import dependency_config_utils + @dataclasses.dataclass class PrepareRunnersReadConfigsHandlerConfig(code_action.ActionHandlerConfig): ... class PrepareRunnersReadConfigsHandler( - code_action.ActionHandler[prepare_runners_action.PrepareRunnersAction, PrepareRunnersReadConfigsHandlerConfig] + code_action.ActionHandler[ + prepare_runners_action.PrepareRunnersAction, + PrepareRunnersReadConfigsHandlerConfig, + ] ): - def __init__(self, action_runner: iactionrunner.IActionRunner, project_info_provider: iprojectinfoprovider.IProjectInfoProvider, logger: ilogger.ILogger) -> None: + def __init__( + self, + action_runner: iactionrunner.IActionRunner, + project_info_provider: iprojectinfoprovider.IProjectInfoProvider, + logger: ilogger.ILogger, + ) -> None: self.action_runner = action_runner self.project_info_provider = project_info_provider self.logger = logger - + async def run( - self, payload: prepare_runners_action.PrepareRunnersRunPayload, run_context: prepare_runners_action.PrepareRunnersRunContext + self, + payload: prepare_runners_action.PrepareRunnersRunPayload, + run_context: prepare_runners_action.PrepareRunnersRunContext, ) -> prepare_runners_action.PrepareRunnersRunResult: - project_defs_pathes = set([env_info.project_def_path for env_info in payload.envs]) + project_defs_pathes = set( + [env_info.project_def_path for env_info in payload.envs] + ) if len(project_defs_pathes) != 1: - ... # TODO: error - + ... # TODO: error + project_raw_config = await self.project_info_provider.get_project_raw_config() - + project_def_path = project_defs_pathes.pop() project_dir_path = project_def_path.parent - - dependency_config_utils.make_project_config_pip_compatible(project_raw_config, project_def_path) - + + dependency_config_utils.make_project_config_pip_compatible( + project_raw_config, project_def_path + ) + for env_info in payload.envs: - run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = project_def_path - run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = project_raw_config - + run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = ( + project_def_path + ) + run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = ( + project_raw_config + ) + return prepare_runners_action.PrepareRunnersRunResult(errors=[]) diff --git a/finecode_extension_runner/src/finecode_extension_runner/cli.py b/finecode_extension_runner/src/finecode_extension_runner/cli.py index 43af2f1..83bebcb 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/cli.py +++ b/finecode_extension_runner/src/finecode_extension_runner/cli.py @@ -1,7 +1,7 @@ import os import sys -from pathlib import Path from importlib import metadata +from pathlib import Path import click from loguru import logger @@ -27,7 +27,9 @@ def main(): required=True, ) @click.option("--env-name", "env_name", type=str) -def start(trace: bool, debug: bool, debug_port: int, project_path: Path, env_name: str | None): +def start( + trace: bool, debug: bool, debug_port: int, project_path: Path, env_name: str | None +): if debug is True: import debugpy @@ -55,8 +57,8 @@ def start(trace: bool, debug: bool, debug_port: int, project_path: Path, env_nam @main.command() def version(): """Show version information""" - package_version = metadata.version('finecode_extension_runner') - click.echo(f'FineCode Extension Runner {package_version}') + package_version = metadata.version("finecode_extension_runner") + click.echo(f"FineCode Extension Runner {package_version}") if __name__ == "__main__": diff --git a/finecode_extension_runner/src/finecode_extension_runner/context.py b/finecode_extension_runner/src/finecode_extension_runner/context.py index 6a54029..baf4844 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/context.py +++ b/finecode_extension_runner/src/finecode_extension_runner/context.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field -from finecode_extension_runner import domain from finecode_extension_api import code_action +from finecode_extension_runner import domain @dataclass diff --git a/finecode_extension_runner/src/finecode_extension_runner/di/_state.py b/finecode_extension_runner/src/finecode_extension_runner/di/_state.py index 7c3bac8..4ef45f9 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/di/_state.py +++ b/finecode_extension_runner/src/finecode_extension_runner/di/_state.py @@ -1,5 +1,4 @@ import typing - container: dict[str, typing.Any] = {} factories: dict[str, typing.Callable] = {} diff --git a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py index 00c8c03..64493d8 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py +++ b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py @@ -1,4 +1,3 @@ - from typing import Any, Callable, Type, TypeVar try: @@ -11,15 +10,6 @@ except ImportError: fine_python_mypy = None -from finecode_extension_runner import global_state -from finecode_extension_runner.impls import ( - action_runner, - command_runner, - file_manager, - inmemory_cache, - loguru_logger, - project_info_provider -) from finecode_extension_api import code_action from finecode_extension_api.interfaces import ( iactionrunner, @@ -27,11 +17,20 @@ icommandrunner, ifilemanager, ilogger, - iprojectinfoprovider + iprojectinfoprovider, ) -from ._state import container, factories -from finecode_extension_runner import schemas +from finecode_extension_runner import global_state, schemas from finecode_extension_runner._services import run_action +from finecode_extension_runner.impls import ( + action_runner, + command_runner, + file_manager, + inmemory_cache, + loguru_logger, + project_info_provider, +) + +from ._state import container, factories def bootstrap(get_document_func: Callable, save_document_func: Callable): @@ -47,24 +46,33 @@ def bootstrap(get_document_func: Callable, save_document_func: Callable): cache_instance = inmemory_cache.InMemoryCache( file_manager=file_manager_instance, logger=logger_instance ) - action_runner_instance = action_runner.ActionRunner(internal_service_func=run_action_wrapper) + action_runner_instance = action_runner.ActionRunner( + internal_service_func=run_action_wrapper + ) container[ilogger.ILogger] = logger_instance container[icommandrunner.ICommandRunner] = command_runner_instance container[ifilemanager.IFileManager] = file_manager_instance container[icache.ICache] = cache_instance container[iactionrunner.IActionRunner] = action_runner_instance - + if fine_python_ast is not None: - factories[fine_python_ast.IPythonSingleAstProvider] = python_single_ast_provider_factory + factories[fine_python_ast.IPythonSingleAstProvider] = ( + python_single_ast_provider_factory + ) if fine_python_mypy is not None: - factories[fine_python_mypy.IMypySingleAstProvider] = mypy_single_ast_provider_factory + factories[fine_python_mypy.IMypySingleAstProvider] = ( + mypy_single_ast_provider_factory + ) factories[iprojectinfoprovider.IProjectInfoProvider] = project_info_provider_factory # TODO: parameters from config -async def run_action_wrapper(action_name: str, payload: dict[str, Any]) -> dict[str, Any]: + +async def run_action_wrapper( + action_name: str, payload: dict[str, Any] +) -> dict[str, Any]: request = schemas.RunActionRequest(action_name=action_name, params=payload) - options = schemas.RunActionOptions(result_format='json') + options = schemas.RunActionOptions(result_format="json") # TODO: map exceptions to iactionrunner response = await run_action.run_action(request=request, options=options) @@ -77,7 +85,8 @@ def python_single_ast_provider_factory(container): cache=container[icache.ICache], logger=container[ilogger.ILogger], ) - + + def mypy_single_ast_provider_factory(container): return fine_python_mypy.MypySingleAstProvider( file_manager=container[ifilemanager.IFileManager], diff --git a/finecode_extension_runner/src/finecode_extension_runner/di/resolver.py b/finecode_extension_runner/src/finecode_extension_runner/di/resolver.py index fb5e4e7..f9d10e2 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/di/resolver.py +++ b/finecode_extension_runner/src/finecode_extension_runner/di/resolver.py @@ -1,6 +1,7 @@ from typing import Any, Callable, Type, TypeVar from finecode_extension_api import code_action + from ._state import container, factories T = TypeVar("T") diff --git a/finecode_extension_runner/src/finecode_extension_runner/domain.py b/finecode_extension_runner/src/finecode_extension_runner/domain.py index fd5b97c..c422f5a 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/domain.py +++ b/finecode_extension_runner/src/finecode_extension_runner/domain.py @@ -4,8 +4,8 @@ import typing from pathlib import Path -from finecode_extension_runner.impls import process_executor as process_executor_impl from finecode_extension_api import code_action +from finecode_extension_runner.impls import process_executor as process_executor_impl class Action: @@ -35,7 +35,7 @@ def __init__( name: str, path: Path, actions: dict[str, Action], - action_handler_configs: dict[str, dict[str, typing.Any]] + action_handler_configs: dict[str, dict[str, typing.Any]], ) -> None: self.name = name self.path = path diff --git a/finecode_extension_runner/src/finecode_extension_runner/impls/action_runner.py b/finecode_extension_runner/src/finecode_extension_runner/impls/action_runner.py index dda1271..1b39c80 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/impls/action_runner.py +++ b/finecode_extension_runner/src/finecode_extension_runner/impls/action_runner.py @@ -5,13 +5,8 @@ class ActionRunner(iactionrunner.IActionRunner): - def __init__( - self, - internal_service_func - ): + def __init__(self, internal_service_func): self._internal_service_func = internal_service_func - async def run_action( - self, name: str, payload: dict[str, Any] - ) -> dict[str, Any]: + async def run_action(self, name: str, payload: dict[str, Any]) -> dict[str, Any]: return await self._internal_service_func(action_name=name, payload=payload) diff --git a/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py b/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py index 3dae9a1..70ee436 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py +++ b/finecode_extension_runner/src/finecode_extension_runner/impls/file_manager.py @@ -3,8 +3,8 @@ from pathlib import Path from typing import Callable -from finecode_extension_runner import domain from finecode_extension_api.interfaces import ifilemanager, ilogger +from finecode_extension_runner import domain class FileManager(ifilemanager.IFileManager): @@ -75,7 +75,9 @@ async def save_file(self, file_path: Path, file_content: str) -> None: with open(file_path, "w") as f: f.write(file_content) - async def create_dir(self, dir_path: Path, create_parents: bool = True, exist_ok: bool = True): + async def create_dir( + self, dir_path: Path, create_parents: bool = True, exist_ok: bool = True + ): # currently only local file system is supported dir_path.mkdir(parents=create_parents, exist_ok=exist_ok) diff --git a/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py b/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py index 6b8d8c9..92e2f0e 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py +++ b/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py @@ -1,19 +1,14 @@ -from typing import Callable -from typing import Any +from typing import Any, Callable from finecode_extension_api.interfaces import iprojectinfoprovider - project_raw_config_getter: Callable class ProjectInfoProvider(iprojectinfoprovider.IProjectInfoProvider): def __init__( self, - ) -> None: - ... + ) -> None: ... - async def get_project_raw_config( - self - ) -> dict[str, Any]: + async def get_project_raw_config(self) -> dict[str, Any]: return await project_raw_config_getter() diff --git a/finecode_extension_runner/src/finecode_extension_runner/logs.py b/finecode_extension_runner/src/finecode_extension_runner/logs.py index 0180c70..a9ea3dd 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/logs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/logs.py @@ -70,8 +70,4 @@ def reset_log_level_for_group(group: str): del log_level_by_group[group] -__all__ = [ - 'save_logs_to_file', - 'set_log_level_for_group', - 'reset_log_level_for_group' -] \ No newline at end of file +__all__ = ["save_logs_to_file", "set_log_level_for_group", "reset_log_level_for_group"] diff --git a/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py index 4c50383..98cec46 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py +++ b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py @@ -8,6 +8,7 @@ import atexit import dataclasses import json +import pathlib import time import pygls.exceptions as pygls_exceptions @@ -15,10 +16,10 @@ from lsprotocol import types from pygls.lsp import server as lsp_server +from finecode_extension_api import code_action from finecode_extension_runner import domain, schemas, services from finecode_extension_runner._services import run_action as run_action_service from finecode_extension_runner.impls import project_info_provider -from finecode_extension_api import code_action class CustomLanguageServer(lsp_server.LanguageServer): @@ -118,24 +119,22 @@ def send_partial_result( logger.debug(f"Send partial result for {token}") partial_result_dict = dataclasses.asdict(partial_result) partial_result_json = json.dumps(partial_result_dict) - server.progress( - types.ProgressParams(token=token, value=partial_result_json) - ) + server.progress(types.ProgressParams(token=token, value=partial_result_json)) services.document_requester = document_requester services.document_saver = document_saver run_action_service.set_partial_result_sender(send_partial_result) - + async def get_project_raw_config(): try: raw_config = await server.protocol.send_request_async( "projects/getRawConfig", params={} ) except pygls_exceptions.JsonRpcInternalError as error: - raise error + raise error return json.loads(raw_config.config) - + project_info_provider.project_raw_config_getter = get_project_raw_config return server @@ -170,8 +169,8 @@ async def update_config(ls: lsp_server.LanguageServer, params): working_dir = params[0] project_name = params[1] config = params[2] - actions = config['actions'] - action_handler_configs = config['action_handler_configs'] + actions = config["actions"] + action_handler_configs = config["action_handler_configs"] request = schemas.UpdateConfigRequest( working_dir=working_dir, @@ -192,7 +191,7 @@ async def update_config(ls: lsp_server.LanguageServer, params): ) for action in actions }, - action_handler_configs=action_handler_configs + action_handler_configs=action_handler_configs, ) response = await services.update_config(request=request) return response.to_dict() @@ -201,17 +200,25 @@ async def update_config(ls: lsp_server.LanguageServer, params): raise e +class CustomJSONEncoder(json.JSONEncoder): + # add support of serializing pathes to json.dumps + def default(self, obj): + if isinstance(obj, (pathlib.Path, pathlib.PosixPath, pathlib.WindowsPath)): + return str(obj) + return super().default(obj) + + async def run_action(ls: lsp_server.LanguageServer, params): logger.trace(f"Run action: {params[0]}") request = schemas.RunActionRequest(action_name=params[0], params=params[1]) options = schemas.RunActionOptions(**params[2] if params[2] is not None else {}) - status: str = 'success' + status: str = "success" try: response = await services.run_action(request=request, options=options) except Exception as exception: if isinstance(exception, services.StopWithResponse): - status = 'stopped' + status = "stopped" response = exception.response else: error_msg = "" @@ -221,14 +228,12 @@ async def run_action(ls: lsp_server.LanguageServer, params): else: logger.error("Unhandled exception in action run:") logger.exception(exception) - error_msg = f'{type(exception)}: {str(exception)}' - return { - "error": error_msg - } + error_msg = f"{type(exception)}: {str(exception)}" + return {"error": error_msg} # dict key can be path, but pygls fails to handle slashes in dict keys, use strings # representation of result instead until the problem is properly solved - result_str = json.dumps(response.to_dict()["result"]) + result_str = json.dumps(response.to_dict()["result"], cls=CustomJSONEncoder) return { "status": status, "result": result_str, diff --git a/finecode_extension_runner/src/finecode_extension_runner/services.py b/finecode_extension_runner/src/finecode_extension_runner/services.py index c9a8863..290cdd3 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/services.py +++ b/finecode_extension_runner/src/finecode_extension_runner/services.py @@ -11,14 +11,23 @@ from loguru import logger from pydantic.dataclasses import dataclass as pydantic_dataclass -from finecode_extension_runner import context, domain, global_state -from finecode_extension_runner import project_dirs, run_utils, schemas from finecode_extension_api import code_action, textstyler +from finecode_extension_runner import ( + context, + domain, + global_state, + project_dirs, + run_utils, + schemas, +) from finecode_extension_runner._services import run_action as run_action_module -from finecode_extension_runner._services.run_action import run_action, ActionFailedException, StopWithResponse +from finecode_extension_runner._services.run_action import ( + ActionFailedException, + StopWithResponse, + run_action, +) from finecode_extension_runner.di import bootstrap as di_bootstrap - document_requester: typing.Callable document_saver: typing.Callable @@ -61,7 +70,7 @@ async def update_config( name=request.project_name, path=project_path, actions=actions, - action_handler_configs=request.action_handler_configs + action_handler_configs=request.action_handler_configs, ), ) diff --git a/finecode_extension_runner/src/finecode_extension_runner/start.py b/finecode_extension_runner/src/finecode_extension_runner/start.py index 8bf7ee3..904868a 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/start.py +++ b/finecode_extension_runner/src/finecode_extension_runner/start.py @@ -62,7 +62,11 @@ def start_runner_sync(env_name: str) -> None: # to stdout as well~~. See README.md assert global_state.project_dir_path is not None logs.save_logs_to_file( - file_path=global_state.project_dir_path / '.venvs' / env_name / 'logs' / "runner.log", + file_path=global_state.project_dir_path + / ".venvs" + / env_name + / "logs" + / "runner.log", log_level=global_state.log_level, stdout=False, ) diff --git a/presets/fine_python_format/setup.py b/presets/fine_python_format/setup.py index 2196206..435d2a1 100644 --- a/presets/fine_python_format/setup.py +++ b/presets/fine_python_format/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_format", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/presets/fine_python_lint/setup.py b/presets/fine_python_lint/setup.py index bfad464..3779603 100644 --- a/presets/fine_python_lint/setup.py +++ b/presets/fine_python_lint/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_lint", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/presets/fine_python_recommended/setup.py b/presets/fine_python_recommended/setup.py index 128ad4f..1eb7ee2 100644 --- a/presets/fine_python_recommended/setup.py +++ b/presets/fine_python_recommended/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="fine_python_recommended", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/setup.py b/setup.py index acf765f..0a949ac 100644 --- a/setup.py +++ b/setup.py @@ -1,58 +1,67 @@ +import atexit +import shutil +import sys +import tempfile + from setuptools import setup from setuptools.command.build import build from setuptools.command.build_ext import build_ext from setuptools.command.build_py import build_py from setuptools.command.egg_info import egg_info -import tempfile -import atexit -import shutil -import sys # Create a single temp directory for all build operations _TEMP_BUILD_DIR = None + def get_temp_build_dir(pkg_name): global _TEMP_BUILD_DIR if _TEMP_BUILD_DIR is None: - _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f'{pkg_name}_build_') + _TEMP_BUILD_DIR = tempfile.mkdtemp(prefix=f"{pkg_name}_build_") atexit.register(lambda: shutil.rmtree(_TEMP_BUILD_DIR, ignore_errors=True)) return _TEMP_BUILD_DIR + class TempDirBuildMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.build_base = temp_dir + class TempDirEggInfoMixin: def initialize_options(self): super().initialize_options() temp_dir = get_temp_build_dir(self.distribution.get_name()) self.egg_base = temp_dir + class CustomBuild(TempDirBuildMixin, build): pass + class CustomBuildPy(TempDirBuildMixin, build_py): pass + class CustomBuildExt(TempDirBuildMixin, build_ext): pass + class CustomEggInfo(TempDirEggInfoMixin, egg_info): def initialize_options(self): # Don't use temp dir for editable installs - if '--editable' in sys.argv or '-e' in sys.argv: + if "--editable" in sys.argv or "-e" in sys.argv: egg_info.initialize_options(self) else: super().initialize_options() + setup( name="finecode", cmdclass={ - 'build': CustomBuild, - 'build_py': CustomBuildPy, - 'build_ext': CustomBuildExt, - 'egg_info': CustomEggInfo, - } -) \ No newline at end of file + "build": CustomBuild, + "build_py": CustomBuildPy, + "build_ext": CustomBuildExt, + "egg_info": CustomEggInfo, + }, +) diff --git a/src/finecode/cli.py b/src/finecode/cli.py index 05fbbfa..b8c5989 100644 --- a/src/finecode/cli.py +++ b/src/finecode/cli.py @@ -7,11 +7,10 @@ from loguru import logger import finecode.main as workspace_manager -from finecode import communication_utils -from finecode import logger_utils, user_messages -from finecode.cli_app import run as run_cmd +from finecode import communication_utils, logger_utils, user_messages from finecode.cli_app import dump_config as dump_config_cmd from finecode.cli_app import prepare_envs as prepare_envs_cmd +from finecode.cli_app import run as run_cmd @click.group() @@ -33,7 +32,9 @@ def cli(): ... "--port", "port", default=None, type=int, help="Port for TCP and WS server" ) @click.option("--mcp", "mcp", is_flag=True, default=False) -@click.option("--mcp-port", "mcp_port", default=None, type=int, help="Port for MCP server") +@click.option( + "--mcp-port", "mcp_port", default=None, type=int, help="Port for MCP server" +) def start_api( trace: bool, debug: bool, @@ -43,7 +44,7 @@ def start_api( host: str | None, port: int | None, mcp: bool, - mcp_port: int | None + mcp_port: int | None, ): if debug is True: import debugpy @@ -78,9 +79,7 @@ def start_api( workspace_manager.start_sync(comm_type=comm_type, host=host, port=port, trace=trace) -async def show_user_message( - message: str, message_type: str -) -> None: +async def show_user_message(message: str, message_type: str) -> None: # user messages in CLI are not needed because CLI outputs own messages ... @@ -179,8 +178,7 @@ def run(ctx) -> None: @click.option("--trace", "trace", is_flag=True, default=False) @click.option("--debug", "debug", is_flag=True, default=False) @click.option("--recreate", "recreate", is_flag=True, default=False) -def prepare_envs(trace: bool, - debug: bool, recreate: bool) -> None: +def prepare_envs(trace: bool, debug: bool, recreate: bool) -> None: """ `prepare-envs` should be called from workspace/project root directory. """ @@ -193,12 +191,16 @@ def prepare_envs(trace: bool, debugpy.wait_for_client() except Exception as e: logger.info(e) - + logger_utils.init_logger(trace=trace, stdout=True) user_messages._notification_sender = show_user_message try: - asyncio.run(prepare_envs_cmd.prepare_envs(workdir_path=pathlib.Path(os.getcwd()), recreate=recreate)) + asyncio.run( + prepare_envs_cmd.prepare_envs( + workdir_path=pathlib.Path(os.getcwd()), recreate=recreate + ) + ) except prepare_envs_cmd.PrepareEnvsFailed as exception: click.echo(exception.args[0], err=True) sys.exit(1) @@ -212,11 +214,7 @@ def prepare_envs(trace: bool, @click.option("--trace", "trace", is_flag=True, default=False) @click.option("--debug", "debug", is_flag=True, default=False) @click.option("--project", "project", type=str) -def dump_config( - trace: bool, - debug: bool, - project: str | None -): +def dump_config(trace: bool, debug: bool, project: str | None): if debug is True: import debugpy @@ -229,12 +227,16 @@ def dump_config( if project is None: click.echo("--project parameter is required", err=True) return - + logger_utils.init_logger(trace=trace, stdout=True) user_messages._notification_sender = show_user_message try: - asyncio.run(dump_config_cmd.dump_config(workdir_path=pathlib.Path(os.getcwd()), project_name=project)) + asyncio.run( + dump_config_cmd.dump_config( + workdir_path=pathlib.Path(os.getcwd()), project_name=project + ) + ) except dump_config_cmd.DumpFailed as exception: click.echo(exception.message, err=True) sys.exit(1) diff --git a/src/finecode/cli_app/dump_config.py b/src/finecode/cli_app/dump_config.py index 57bfcfd..e489eab 100644 --- a/src/finecode/cli_app/dump_config.py +++ b/src/finecode/cli_app/dump_config.py @@ -4,7 +4,7 @@ from loguru import logger from finecode import context, services -from finecode.config import read_configs, dump_configs +from finecode.config import read_configs from finecode.runner import manager as runner_manager @@ -27,7 +27,7 @@ async def dump_config(workdir_path: pathlib.Path, project_name: str): for project_dir_path, project in ws_context.ws_projects.items() if project.name == project_name } - + # start runner to init project config try: try: @@ -41,18 +41,22 @@ async def dump_config(workdir_path: pathlib.Path, project_name: str): # Some tools like IDE extensions for syntax highlighting rely on # file name. Keep file name of config the same and save in subdirectory project_dir_path = list(ws_context.ws_projects.keys())[0] - dump_dir_path = project_dir_path / 'finecode_config_dump' - dump_file_path = dump_dir_path / 'pyproject.toml' + dump_dir_path = project_dir_path / "finecode_config_dump" + dump_file_path = dump_dir_path / "pyproject.toml" project_raw_config = ws_context.ws_projects_raw_configs[project_dir_path] project_def = ws_context.ws_projects[project_dir_path] - + await services.run_action( - action_name='dump_config', - params={"source_file_path": project_def.def_path, "project_raw_config": project_raw_config, "target_file_path": dump_file_path }, + action_name="dump_config", + params={ + "source_file_path": project_def.def_path, + "project_raw_config": project_raw_config, + "target_file_path": dump_file_path, + }, project_def=project_def, ws_context=ws_context, result_format=services.RunResultFormat.STRING, - preprocess_payload=False + preprocess_payload=False, ) logger.info(f"Dumped config into {dump_file_path}") finally: diff --git a/src/finecode/cli_app/prepare_envs.py b/src/finecode/cli_app/prepare_envs.py index 233ce49..ebab8ce 100644 --- a/src/finecode/cli_app/prepare_envs.py +++ b/src/finecode/cli_app/prepare_envs.py @@ -1,15 +1,15 @@ import pathlib import shutil + from loguru import logger -from finecode import context, services, domain -from finecode.config import read_configs, collect_actions, config_models +from finecode import context, domain, services from finecode.cli_app import run as run_cli +from finecode.config import collect_actions, config_models, read_configs from finecode.runner import manager as runner_manager -class PrepareEnvsFailed(Exception): - ... +class PrepareEnvsFailed(Exception): ... async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: @@ -22,25 +22,43 @@ async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: await read_configs.read_projects_in_dir( dir_path=workdir_path, ws_context=ws_context ) - + # `prepare_envs` can be run only from workspace/project root. Validate this if workdir_path not in ws_context.ws_projects: - raise PrepareEnvsFailed("prepare_env can be run only from workspace/project root") + raise PrepareEnvsFailed( + "prepare_env can be run only from workspace/project root" + ) - invalid_projects = [project for project in ws_context.ws_projects.values() if project.status == domain.ProjectStatus.CONFIG_INVALID] + invalid_projects = [ + project + for project in ws_context.ws_projects.values() + if project.status == domain.ProjectStatus.CONFIG_INVALID + ] if len(invalid_projects) > 0: - raise PrepareEnvsFailed(f"Projects have invalid configuration: {invalid_projects}") + raise PrepareEnvsFailed( + f"Projects have invalid configuration: {invalid_projects}" + ) # prepare envs only in projects with valid configurations and which use finecode - projects = [project for project in ws_context.ws_projects.values() if project.status == domain.ProjectStatus.CONFIG_VALID] - + projects = [ + project + for project in ws_context.ws_projects.values() + if project.status == domain.ProjectStatus.CONFIG_VALID + ] + # Collect actions in relevant projects for project in projects: try: - await read_configs.read_project_config(project=project, ws_context=ws_context, resolve_presets=False) - collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + await read_configs.read_project_config( + project=project, ws_context=ws_context, resolve_presets=False + ) + collect_actions.collect_actions( + project_path=project.dir_path, ws_context=ws_context + ) except config_models.ConfigurationError as exception: - raise PrepareEnvsFailed(f"Reading project config and collecting actions in {project.dir_path} failed: {exception.message}") + raise PrepareEnvsFailed( + f"Reading project config and collecting actions in {project.dir_path} failed: {exception.message}" + ) try: # try to start runner in 'dev_workspace' env of each project. If venv doesn't @@ -48,22 +66,31 @@ async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: if recreate: remove_dev_workspace_envs(projects=projects, workdir_path=workdir_path) - await check_or_recreate_all_dev_workspace_envs(projects=projects, workdir_path=workdir_path, recreate=recreate, ws_context=ws_context) - + await check_or_recreate_all_dev_workspace_envs( + projects=projects, + workdir_path=workdir_path, + recreate=recreate, + ws_context=ws_context, + ) + # now all 'dev_workspace' envs are valid, run 'prepare_runners' in them to create # venvs and install runners and presets in them. This is required to be able to # resolve presets which can contain additional dependency configurations. # Only then run `prepare_envs` to install dependencies in each subproject. - actions_by_projects: dict[pathlib.Path, list[str]] = {project.dir_path: ['prepare_runners'] for project in projects} + actions_by_projects: dict[pathlib.Path, list[str]] = { + project.dir_path: ["prepare_runners"] for project in projects + } # action payload can be kept empty because it will be filled in payload preprocessor action_payload: dict[str, str | bool] = {"recreate": recreate} - + await run_cli.start_required_environments(actions_by_projects, ws_context) - + try: - (result_output, result_return_code) = await run_cli.run_actions_in_all_projects( - actions_by_projects, action_payload, ws_context, concurrently=True + (result_output, result_return_code) = ( + await run_cli.run_actions_in_all_projects( + actions_by_projects, action_payload, ws_context, concurrently=True + ) ) except run_cli.RunFailed as error: logger.error(error.message) @@ -76,24 +103,38 @@ async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: # reread projects configs, now with resolved presets # to be able to resolve presets, start dev_no_runtime runners first try: - await runner_manager.start_runners_with_presets(projects=projects, ws_context=ws_context) + await runner_manager.start_runners_with_presets( + projects=projects, ws_context=ws_context + ) except runner_manager.RunnerFailedToStart as exception: - raise PrepareEnvsFailed(f"Starting runners with presets failed: {exception.message}") - + raise PrepareEnvsFailed( + f"Starting runners with presets failed: {exception.message}" + ) + for project in projects: try: - await read_configs.read_project_config(project=project, ws_context=ws_context) - collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + await read_configs.read_project_config( + project=project, ws_context=ws_context + ) + collect_actions.collect_actions( + project_path=project.dir_path, ws_context=ws_context + ) except config_models.ConfigurationError as exception: - raise PrepareEnvsFailed(f"Rereading project config with presets and collecting actions in {project.dir_path} failed: {exception.message}") - - actions_by_projects: dict[pathlib.Path, list[str]] = {project.dir_path: ['prepare_envs'] for project in projects} + raise PrepareEnvsFailed( + f"Rereading project config with presets and collecting actions in {project.dir_path} failed: {exception.message}" + ) + + actions_by_projects: dict[pathlib.Path, list[str]] = { + project.dir_path: ["prepare_envs"] for project in projects + } # action payload can be kept empty because it will be filled in payload preprocessor action_payload: dict[str, str | bool] = {"recreate": recreate} try: - (result_output, result_return_code) = await run_cli.run_actions_in_all_projects( - actions_by_projects, action_payload, ws_context, concurrently=True + (result_output, result_return_code) = ( + await run_cli.run_actions_in_all_projects( + actions_by_projects, action_payload, ws_context, concurrently=True + ) ) except run_cli.RunFailed as error: logger.error(error.message) @@ -106,52 +147,66 @@ async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: services.on_shutdown(ws_context) -def remove_dev_workspace_envs(projects: list[domain.Project], workdir_path: pathlib.Path) -> None: +def remove_dev_workspace_envs( + projects: list[domain.Project], workdir_path: pathlib.Path +) -> None: for project in projects: if project.dir_path == workdir_path: # skip removing `dev_workspace` env of the current project, because user # is responsible for keeping it correct continue - - runner_manager.remove_runner_venv(runner_dir=project.dir_path, env_name='dev_workspace') + + runner_manager.remove_runner_venv( + runner_dir=project.dir_path, env_name="dev_workspace" + ) -async def check_or_recreate_all_dev_workspace_envs(projects: list[domain.Project], workdir_path: pathlib.Path, recreate: bool, ws_context: context.WorkspaceContext) -> None: +async def check_or_recreate_all_dev_workspace_envs( + projects: list[domain.Project], + workdir_path: pathlib.Path, + recreate: bool, + ws_context: context.WorkspaceContext, +) -> None: # NOTE: this function can start new extensions runner, don't forget to call # on_shutdown if you use it projects_dirs_with_valid_envs: list[pathlib.Path] = [] projects_dirs_with_invalid_envs: list[pathlib.Path] = [] - + for project in projects: if project.dir_path == workdir_path: # skip checking `dev_workspace` env of the current project, because user # is responsible for keeping it correct continue - + runner_is_valid = await runner_manager.check_runner( - runner_dir=project.dir_path, - env_name='dev_workspace' + runner_dir=project.dir_path, env_name="dev_workspace" ) if runner_is_valid: projects_dirs_with_valid_envs.append(project.dir_path) else: if recreate: - logger.trace(f"Recreate runner for env 'dev_workspace' in project '{project.name}'") + logger.trace( + f"Recreate runner for env 'dev_workspace' in project '{project.name}'" + ) else: - logger.warning(f"Runner for env 'dev_workspace' in project '{project.name}' is invalid, recreate it") + logger.warning( + f"Runner for env 'dev_workspace' in project '{project.name}' is invalid, recreate it" + ) projects_dirs_with_invalid_envs.append(project.dir_path) # to recreate dev_workspace env, run `prepare_envs` in runner of current project current_project_dir_path = ws_context.ws_dirs_paths[0] current_project = ws_context.ws_projects[current_project_dir_path] try: - runner = await runner_manager.start_runner(project_def=current_project, env_name='dev_workspace', ws_context=ws_context) + runner = await runner_manager.start_runner( + project_def=current_project, env_name="dev_workspace", ws_context=ws_context + ) except runner_manager.RunnerFailedToStart as exception: # TODO raise exception envs = [] - + # run pip install in dev_workspace even if env exists to make sure that correct # dependencies are installed for project_dir_path in projects_dirs_with_valid_envs: @@ -160,10 +215,16 @@ async def check_or_recreate_all_dev_workspace_envs(projects: list[domain.Project # current project, because user is responsible for keeping them # up-to-date continue - + # dependencies in `dev_workspace` should be simple and installable without # dumping - envs.append({"name": "dev_workspace", "venv_dir_path": project_dir_path / '.venvs' / 'dev_workspace', "project_def_path": project_dir_path / 'pyproject.toml' }) + envs.append( + { + "name": "dev_workspace", + "venv_dir_path": project_dir_path / ".venvs" / "dev_workspace", + "project_def_path": project_dir_path / "pyproject.toml", + } + ) if len(projects_dirs_with_invalid_envs) > 0: invalid_envs = [] @@ -171,25 +232,35 @@ async def check_or_recreate_all_dev_workspace_envs(projects: list[domain.Project for project_dir_path in projects_dirs_with_invalid_envs: # dependencies in `dev_workspace` should be simple and installable without # dumping - invalid_envs.append({"name": "dev_workspace", "venv_dir_path": project_dir_path / '.venvs' / 'dev_workspace', "project_def_path": project_dir_path / 'pyproject.toml' }) - + invalid_envs.append( + { + "name": "dev_workspace", + "venv_dir_path": project_dir_path / ".venvs" / "dev_workspace", + "project_def_path": project_dir_path / "pyproject.toml", + } + ) + # remove existing invalid envs for env_info in invalid_envs: if env_info["venv_dir_path"].exists(): logger.trace(f"{env_info['venv_dir_path']} was invalid, remove it") shutil.rmtree(env_info["venv_dir_path"]) - + envs += invalid_envs # TODO: check result try: await services.run_action( - action_name='prepare_dev_workspaces_envs', - params={ "envs": envs, }, + action_name="prepare_dev_workspaces_envs", + params={ + "envs": envs, + }, project_def=current_project, ws_context=ws_context, result_format=services.RunResultFormat.STRING, - preprocess_payload=False + preprocess_payload=False, ) except services.ActionRunFailed as exception: - raise PrepareEnvsFailed(f"'prepare_dev_workspaces_env' failed in {current_project.name}: {exception.message}") + raise PrepareEnvsFailed( + f"'prepare_dev_workspaces_env' failed in {current_project.name}: {exception.message}" + ) diff --git a/src/finecode/cli_app/run.py b/src/finecode/cli_app/run.py index afb06ae..68d735e 100644 --- a/src/finecode/cli_app/run.py +++ b/src/finecode/cli_app/run.py @@ -7,8 +7,9 @@ from loguru import logger from finecode import context, domain, services -from finecode.config import read_configs, collect_actions, config_models -from finecode.runner import manager as runner_manager, runner_info +from finecode.config import collect_actions, config_models, read_configs +from finecode.runner import manager as runner_manager +from finecode.runner import runner_info class RunFailed(Exception): @@ -19,7 +20,7 @@ def __init__(self, message: str) -> None: async def start_required_environments( actions_by_projects: dict[pathlib.Path, list[str]], ws_context: context.WorkspaceContext, - update_config_in_running_runners: bool = False + update_config_in_running_runners: bool = False, ) -> None: """Collect all required envs from actions that will be run and start them.""" required_envs_by_project: dict[pathlib.Path, set[str]] = {} @@ -29,40 +30,51 @@ async def start_required_environments( project_required_envs = set() for action_name in action_names: # find the action and collect envs from its handlers - action = next((a for a in project.actions if a.name == action_name), None) + action = next( + (a for a in project.actions if a.name == action_name), None + ) if action is not None: for handler in action.handlers: project_required_envs.add(handler.env) required_envs_by_project[project_dir_path] = project_required_envs - + # start runners for required environments that aren't already running for project_dir_path, required_envs in required_envs_by_project.items(): project = ws_context.ws_projects[project_dir_path] - existing_runners = ws_context.ws_projects_extension_runners.get(project_dir_path, {}) - + existing_runners = ws_context.ws_projects_extension_runners.get( + project_dir_path, {} + ) + for env_name in required_envs: runner_exist = env_name in existing_runners start_runner = True if runner_exist: - runner_is_running = existing_runners[env_name].status == runner_info.RunnerStatus.RUNNING + runner_is_running = ( + existing_runners[env_name].status + == runner_info.RunnerStatus.RUNNING + ) start_runner = not runner_is_running if start_runner: try: runner = await runner_manager.start_runner( - project_def=project, - env_name=env_name, - ws_context=ws_context + project_def=project, env_name=env_name, ws_context=ws_context ) except Exception as e: - logger.warning(f"Failed to start runner for env '{env_name}' in project '{project.name}': {e}") + logger.warning( + f"Failed to start runner for env '{env_name}' in project '{project.name}': {e}" + ) # TODO: raise error else: if update_config_in_running_runners: runner = existing_runners[env_name] - logger.trace(f"Runner {runner.working_dir_path} {runner.env_name} is running already, update config") + logger.trace( + f"Runner {runner.working_dir_path} {runner.env_name} is running already, update config" + ) # TODO: handle errors - await runner_manager.update_runner_config(runner=runner, project=project) + await runner_manager.update_runner_config( + runner=runner, project=project + ) async def run_actions( @@ -85,23 +97,31 @@ async def run_actions( for project_dir_path, project in ws_context.ws_projects.items() if project.name in projects_names } - + # make sure all projects use finecode config_problem_found = False for project in ws_context.ws_projects.values(): if project.status != domain.ProjectStatus.CONFIG_VALID: if project.status == domain.ProjectStatus.NO_FINECODE: - logger.error(f"You asked to run action in project '{project.name}', but finecode is not used in it(=there is no 'dev_workspace' environment with 'finecode' package in it)") + logger.error( + f"You asked to run action in project '{project.name}', but finecode is not used in it(=there is no 'dev_workspace' environment with 'finecode' package in it)" + ) config_problem_found = True elif project.status == domain.ProjectStatus.CONFIG_INVALID: - logger.error(f"You asked to run action in project '{project.name}', but its configuration is invalid(see logs above for more details)") + logger.error( + f"You asked to run action in project '{project.name}', but its configuration is invalid(see logs above for more details)" + ) config_problem_found = True else: - logger.error(f"You asked to run action in project '{project.name}', but it has unexpected status: {project.status}") + logger.error( + f"You asked to run action in project '{project.name}', but it has unexpected status: {project.status}" + ) config_problem_found = True - + if config_problem_found: - raise RunFailed("There is a problem with configuration. See previous messages for more details") + raise RunFailed( + "There is a problem with configuration. See previous messages for more details" + ) else: # filter out packages that don't use finecode ws_context.ws_projects = { @@ -109,38 +129,47 @@ async def run_actions( for project_dir_path, project in ws_context.ws_projects.items() if project.status != domain.ProjectStatus.NO_FINECODE } - + # check that configuration of packages that use finecode is valid config_problem_found = False for project in ws_context.ws_projects.values(): if project.status == domain.ProjectStatus.CONFIG_VALID: continue elif project.status == domain.ProjectStatus.CONFIG_INVALID: - logger.error(f"Project '{project.name}' has invalid config, see messages above for more details") + logger.error( + f"Project '{project.name}' has invalid config, see messages above for more details" + ) config_problem_found = True else: - logger.error(f"Project '{project.name}' has unexpected status: {project.status}") + logger.error( + f"Project '{project.name}' has unexpected status: {project.status}" + ) config_problem_found = True - + if config_problem_found: - raise RunFailed("There is a problem with configuration. See previous messages for more details") + raise RunFailed( + "There is a problem with configuration. See previous messages for more details" + ) projects: list[domain.Project] = [] if projects_names is not None: - projects = get_projects_by_names( - projects_names, ws_context, workdir_path - ) + projects = get_projects_by_names(projects_names, ws_context, workdir_path) else: projects = list(ws_context.ws_projects.values()) # first read configs without presets to be able to start runners with presets for project in projects: try: - await read_configs.read_project_config(project=project, ws_context=ws_context, resolve_presets=False) - collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + await read_configs.read_project_config( + project=project, ws_context=ws_context, resolve_presets=False + ) + collect_actions.collect_actions( + project_path=project.dir_path, ws_context=ws_context + ) except config_models.ConfigurationError as exception: - raise RunFailed(f"Reading project config and collecting actions in {project.dir_path} failed: {exception.message}") - + raise RunFailed( + f"Reading project config and collecting actions in {project.dir_path} failed: {exception.message}" + ) try: # 1. Start runners with presets to be able to resolve presets. Presets are @@ -159,8 +188,12 @@ async def run_actions( # 2. Collect actions in relevant projects for project in projects: try: - await read_configs.read_project_config(project=project, ws_context=ws_context) - collect_actions.collect_actions(project_path=project.dir_path, ws_context=ws_context) + await read_configs.read_project_config( + project=project, ws_context=ws_context + ) + collect_actions.collect_actions( + project_path=project.dir_path, ws_context=ws_context + ) except config_models.ConfigurationError as exception: raise RunFailed(f"Found configuration problem: {exception.message}") @@ -184,7 +217,9 @@ async def run_actions( # actions will be run in all projects inside actions_by_projects = find_projects_with_actions(ws_context, actions) - await start_required_environments(actions_by_projects, ws_context, update_config_in_running_runners=True) + await start_required_environments( + actions_by_projects, ws_context, update_config_in_running_runners=True + ) return await run_actions_in_all_projects( actions_by_projects, action_payload, ws_context, concurrently @@ -224,8 +259,8 @@ def find_projects_with_actions( for project in ws_context.ws_projects.values(): project_actions_names = [action.name for action in project.actions] # find which of requested actions are available in the project - action_to_run_in_project = ( - actions_set & ordered_set.OrderedSet(project_actions_names) + action_to_run_in_project = actions_set & ordered_set.OrderedSet( + project_actions_names ) relevant_actions_in_project = list(action_to_run_in_project) if len(relevant_actions_in_project) > 0: @@ -336,7 +371,7 @@ async def run_actions_in_running_project( except ExceptionGroup as eg: for exception in eg.exceptions: if isinstance(exception, services.ActionRunFailed): - logger.error(f'{exception.message} in {project.name}') + logger.error(f"{exception.message} in {project.name}") else: logger.error("Unexpected exception:") logger.exception(exception) @@ -413,7 +448,9 @@ async def run_actions_in_all_projects( result_output += "\n" if run_in_many_projects: - result_output += f"{click.style(str(project_dir_path), bold=True, underline=True)}\n" + result_output += ( + f"{click.style(str(project_dir_path), bold=True, underline=True)}\n" + ) for action_name, action_result in result_by_action.items(): if run_many_actions: diff --git a/src/finecode/config/collect_actions.py b/src/finecode/config/collect_actions.py index 24e6ea3..f43c013 100644 --- a/src/finecode/config/collect_actions.py +++ b/src/finecode/config/collect_actions.py @@ -35,14 +35,16 @@ def collect_actions( return actions -def _collect_action_handler_configs_in_config(config: dict[str, Any]) -> dict[str, dict[str, Any]]: +def _collect_action_handler_configs_in_config( + config: dict[str, Any], +) -> dict[str, dict[str, Any]]: action_handlers_configs = config["tool"]["finecode"].get("action_handler", []) action_handler_config_by_source: dict[str, dict[str, Any]] = {} for handler_def in action_handlers_configs: # TODO: validate that source field exist? - handler_config = handler_def.get('config', None) + handler_config = handler_def.get("config", None) if handler_config is not None: - action_handler_config_by_source[handler_def['source']] = handler_config + action_handler_config_by_source[handler_def["source"]] = handler_config return action_handler_config_by_source @@ -68,7 +70,7 @@ def _collect_actions_in_config( source=handler.source, config=handler.config or {}, env=handler.env, - dependencies=handler.dependencies + dependencies=handler.dependencies, ) for handler in action_def.handlers ], diff --git a/src/finecode/config/dump_configs.py b/src/finecode/config/dump_configs.py deleted file mode 100644 index 939edcc..0000000 --- a/src/finecode/config/dump_configs.py +++ /dev/null @@ -1,6 +0,0 @@ -import typing -import tomlkit - - -def dump_config(config: dict[str, typing.Any]) -> str: - return tomlkit.dumps(config) diff --git a/src/finecode/config/read_configs.py b/src/finecode/config/read_configs.py index 3beb02d..e5f9302 100644 --- a/src/finecode/config/read_configs.py +++ b/src/finecode/config/read_configs.py @@ -1,6 +1,6 @@ +from importlib import metadata from pathlib import Path from typing import Any, NamedTuple -from importlib import metadata from loguru import logger from tomlkit import loads as toml_loads @@ -25,11 +25,15 @@ async def read_projects_in_dir( # path to definition file relative to workspace directory in which this # definition was found def_file_rel_dir_path = def_file.relative_to(dir_path) - if '__testdata__' in def_file_rel_dir_path.parts: - logger.debug(f"Skip '{def_file}' because it is in test data and it is not a test session") + if "__testdata__" in def_file_rel_dir_path.parts: + logger.debug( + f"Skip '{def_file}' because it is in test data and it is not a test session" + ) continue - if def_file.parent.name == 'finecode_config_dump': - logger.debug(f"Skip '{def_file}' because it is config dump, not real project config") + if def_file.parent.name == "finecode_config_dump": + logger.debug( + f"Skip '{def_file}' because it is config dump, not real project config" + ) continue status = domain.ProjectStatus.CONFIG_VALID @@ -38,9 +42,11 @@ async def read_projects_in_dir( with open(def_file, "rb") as pyproject_file: project_def = toml_loads(pyproject_file.read()).value - dependency_groups = project_def.get('dependency-groups', {}) - dev_workspace_group = dependency_groups.get('dev_workspace', []) - finecode_in_dev_workspace = any(dep for dep in dev_workspace_group if get_dependency_name(dep) == 'finecode') + dependency_groups = project_def.get("dependency-groups", {}) + dev_workspace_group = dependency_groups.get("dev_workspace", []) + finecode_in_dev_workspace = any( + dep for dep in dev_workspace_group if get_dependency_name(dep) == "finecode" + ) if not finecode_in_dev_workspace: status = domain.ProjectStatus.NO_FINECODE actions = [] @@ -51,7 +57,7 @@ async def read_projects_in_dir( def_path=def_file, status=status, actions=actions, - env_configs={} + env_configs={}, ) ws_context.ws_projects[def_file.parent] = new_project new_projects.append(new_project) @@ -71,18 +77,20 @@ def get_dependency_name(dependency_str: str) -> str: def read_env_configs(project_config: dict[str, Any]) -> dict[str, domain.EnvConfig]: env_configs = {} - env_config_section = project_config.get('tool', {}).get('finecode', {}).get('env', {}) + env_config_section = ( + project_config.get("tool", {}).get("finecode", {}).get("env", {}) + ) for env_name, env_raw_config in env_config_section.items(): - if 'runner' in env_raw_config: - runner_raw_config = env_raw_config['runner'] - if 'debug' in runner_raw_config: - debug = runner_raw_config['debug'] + if "runner" in env_raw_config: + runner_raw_config = env_raw_config["runner"] + if "debug" in runner_raw_config: + debug = runner_raw_config["debug"] runner_config = domain.RunnerConfig(debug=debug) env_config = domain.EnvConfig(runner_config=runner_config) env_configs[env_name] = env_config - + # add default configs - deps_groups = project_config.get('dependency-groups', {}) + deps_groups = project_config.get("dependency-groups", {}) for group_name in deps_groups.keys(): if group_name not in env_configs: runner_config = domain.RunnerConfig(debug=False) @@ -93,7 +101,9 @@ def read_env_configs(project_config: dict[str, Any]) -> dict[str, domain.EnvConf async def read_project_config( - project: domain.Project, ws_context: context.WorkspaceContext, resolve_presets: bool = True + project: domain.Project, + ws_context: context.WorkspaceContext, + resolve_presets: bool = True, ) -> None: # this function requires running project extension runner to get configuration # from it @@ -102,57 +112,66 @@ async def read_project_config( # TODO: handle error if toml is invalid project_def = toml_loads(pyproject_file.read()).value # TODO: validate that finecode is installed? - - base_config_path = Path(__file__).parent.parent / 'base_config.toml' + + base_config_path = Path(__file__).parent.parent / "base_config.toml" # TODO: cache instead of reading each time - with open(base_config_path, 'r') as base_config_file: + with open(base_config_path, "r") as base_config_file: base_config = toml_loads(base_config_file.read()).value project_config = {} - _merge_projects_configs(project_config, project.def_path, base_config, base_config_path) + _merge_projects_configs( + project_config, project.def_path, base_config, base_config_path + ) finecode_raw_config = project_def.get("tool", {}).get("finecode", None) if finecode_raw_config and resolve_presets: try: - presets = [config_models.FinecodePresetDefinition(**raw_preset) for raw_preset in finecode_raw_config.get('presets', [])] + presets = [ + config_models.FinecodePresetDefinition(**raw_preset) + for raw_preset in finecode_raw_config.get("presets", []) + ] except config_models.ValidationError as exception: raise config_models.ConfigurationError(str(exception)) # all presets expected to be in `dev_no_runtime` environment project_runners = ws_context.ws_projects_extension_runners[project.dir_path] # TODO: can it be the case that there is no such runner? - dev_no_runtime_runner = project_runners['dev_no_runtime'] + dev_no_runtime_runner = project_runners["dev_no_runtime"] new_config = await collect_config_from_py_presets( presets_sources=[preset.source for preset in presets], def_path=project.def_path, runner=dev_no_runtime_runner, ) if new_config is not None: - _merge_projects_configs(project_config, project.def_path, new_config, project.def_path) + _merge_projects_configs( + project_config, project.def_path, new_config, project.def_path + ) - _merge_projects_configs(project_config, project.def_path, project_def, project.def_path) + _merge_projects_configs( + project_config, project.def_path, project_def, project.def_path + ) # `_merge_projects_configs` merges only finecode config. Copy all other keys as # is for key, value in project_def.items(): - if key != 'tool': + if key != "tool": project_config[key] = value - tool_raw_config = project_def.get('tool', None) + tool_raw_config = project_def.get("tool", None) if tool_raw_config is not None: - if 'tool' not in project_config: - project_config['tool'] = {} - project_tool_config = project_config['tool'] + if "tool" not in project_config: + project_config["tool"] = {} + project_tool_config = project_config["tool"] for key, value in tool_raw_config.items(): - if key != 'finecode': + if key != "finecode": project_tool_config[key] = value # add runtime dependency group if it's not explicitly declared add_runtime_dependency_group_if_new(project_config) - + add_extension_runner_to_dependencies(project_config) - + merge_handlers_dependencies_into_groups(project_config) ws_context.ws_projects_raw_configs[project.dir_path] = project_config - + env_configs = read_env_configs(project_config=project_config) project.env_configs = env_configs else: @@ -199,13 +218,15 @@ def read_preset_config( config_dir_path = config_path.parent flat_path_to_src = config_dir_path / preset_id if flat_path_to_src.exists(): - config_path = flat_path_to_src / 'preset.toml' + config_path = flat_path_to_src / "preset.toml" else: - src_path_to_src = config_dir_path / 'src' / preset_id + src_path_to_src = config_dir_path / "src" / preset_id if src_path_to_src.exists(): - config_path = src_path_to_src / 'preset.toml' + config_path = src_path_to_src / "preset.toml" else: - raise config_models.ConfigurationError(f"preset.toml not found in project '{preset_id}'") + raise config_models.ConfigurationError( + f"preset.toml not found in project '{preset_id}'" + ) with open(config_path, "rb") as preset_toml_file: preset_toml = toml_loads(preset_toml_file.read()).value @@ -241,7 +262,9 @@ async def collect_config_from_py_presets( ) if preset_project_path is None: logger.trace(f"Path of preset {preset.source} not found") - raise config_models.ConfigurationError(f"Path of preset {preset.source} in project {def_path.parent} not found") + raise config_models.ConfigurationError( + f"Path of preset {preset.source} in project {def_path.parent} not found" + ) preset_toml_path = preset_project_path / "preset.toml" preset_toml, preset_config = read_preset_config(preset_toml_path, preset.source) @@ -263,14 +286,18 @@ async def collect_config_from_py_presets( return config -def _merge_object_array_by_key(existing_array: list[dict[str, Any]], new_array: list[dict[str, Any]], key_field: str) -> None: +def _merge_object_array_by_key( + existing_array: list[dict[str, Any]], + new_array: list[dict[str, Any]], + key_field: str, +) -> None: """ Merges object arrays by a specified key field. - + For each object in new_array: - If an object with the same key_field value exists in existing_array, deep merge them - If no object with that key_field value exists, append the new object - + Args: existing_array: The array to merge into new_array: The array to merge from @@ -281,14 +308,14 @@ def _merge_object_array_by_key(existing_array: list[dict[str, Any]], new_array: for i, obj in enumerate(existing_array): if isinstance(obj, dict) and key_field in obj: existing_by_key[obj[key_field]] = i - + # Process each new object for new_obj in new_array: if not isinstance(new_obj, dict) or key_field not in new_obj: # If the object doesn't have the key field, just append it existing_array.append(new_obj) continue - + obj_key = new_obj[key_field] if obj_key in existing_by_key: # Merge with existing object @@ -312,7 +339,12 @@ def _deep_merge_dicts(target: dict[str, Any], source: dict[str, Any]) -> None: target[key] = value -def _merge_projects_configs(config1: dict[str, Any], config1_filepath: Path, config2: dict[str, Any], config2_filepath: Path) -> None: +def _merge_projects_configs( + config1: dict[str, Any], + config1_filepath: Path, + config2: dict[str, Any], + config2_filepath: Path, +) -> None: # merge config2 in config1 without overwriting if "tool" not in config1: config1["tool"] = {} @@ -342,36 +374,42 @@ def _merge_projects_configs(config1: dict[str, Any], config1_filepath: Path, con "config" ] action_config.update(action_info["config"]) - + # Handle handlers array merge by name if "handlers" in action_info: if "handlers" not in tool_finecode_config1[key][action_name]: tool_finecode_config1[key][action_name]["handlers"] = [] - - existing_handlers = tool_finecode_config1[key][action_name]["handlers"] + + existing_handlers = tool_finecode_config1[key][action_name][ + "handlers" + ] new_handlers = action_info["handlers"] - + # Merge handlers by name - _merge_object_array_by_key(existing_handlers, new_handlers, 'name') + _merge_object_array_by_key( + existing_handlers, new_handlers, "name" + ) elif key == "action_handler": # Handle action_handler array merge by source if key not in tool_finecode_config1: tool_finecode_config1[key] = [] - + existing_action_handlers = tool_finecode_config1[key] # Ensure value is a list if isinstance(value, list): new_action_handlers = value # Merge action_handlers by source - _merge_object_array_by_key(existing_action_handlers, new_action_handlers, 'source') + _merge_object_array_by_key( + existing_action_handlers, new_action_handlers, "source" + ) else: # If it's not a list, just set it directly (shouldn't happen with TOML arrays but be safe) tool_finecode_config1[key] = value elif key == "env": - if 'env' not in tool_finecode_config1: - tool_finecode_config1['env'] = {} + if "env" not in tool_finecode_config1: + tool_finecode_config1["env"] = {} - all_envs_config1 = tool_finecode_config1['env'] + all_envs_config1 = tool_finecode_config1["env"] for env_name, env_config2 in value.items(): if env_name not in all_envs_config1: @@ -379,49 +417,61 @@ def _merge_projects_configs(config1: dict[str, Any], config1_filepath: Path, con else: # merge env configs env_config1 = all_envs_config1[env_name] - if 'dependencies' in env_config2: - if 'dependencies' not in env_config1: - env_config1['dependencies'] = env_config2['dependencies'] + if "dependencies" in env_config2: + if "dependencies" not in env_config1: + env_config1["dependencies"] = env_config2["dependencies"] else: # merge dependencies - env_config1_deps = env_config1['dependencies'] - for dependency_name, dependency in env_config2['dependencies'].items(): + env_config1_deps = env_config1["dependencies"] + for dependency_name, dependency in env_config2[ + "dependencies" + ].items(): if dependency_name not in env_config1_deps: env_config1_deps[dependency_name] = dependency else: - if 'path' in dependency: - new_path = dependency['path'] - if new_path.startswith('.'): - abs_path = config2_filepath.parent / new_path - new_rel_path = abs_path.relative_to(config1_filepath.parent) + if "path" in dependency: + new_path = dependency["path"] + if new_path.startswith("."): + abs_path = ( + config2_filepath.parent / new_path + ) + new_rel_path = abs_path.relative_to( + config1_filepath.parent + ) new_path = new_rel_path.as_posix() - env_config1_deps[dependency_name]['path'] = new_path - if 'editable' in dependency: - env_config1_deps[dependency_name]['editable'] = dependency['editable'] - if 'runner' in env_config2: - if 'runner' not in env_config1: - env_config1['runner'] = {} - env_config1_runner = env_config1['runner'] - env_config2_runner = env_config2['runner'] - - if 'debug' in env_config2_runner: - env_config1_runner['debug'] = env_config2_runner['debug'] + env_config1_deps[dependency_name][ + "path" + ] = new_path + if "editable" in dependency: + env_config1_deps[dependency_name][ + "editable" + ] = dependency["editable"] + if "runner" in env_config2: + if "runner" not in env_config1: + env_config1["runner"] = {} + env_config1_runner = env_config1["runner"] + env_config2_runner = env_config2["runner"] + + if "debug" in env_config2_runner: + env_config1_runner["debug"] = env_config2_runner["debug"] elif key in config1: tool_finecode_config1[key].update(value) else: tool_finecode_config1[key] = value -def add_action_to_config_if_new(raw_config: dict[str, Any], action: domain.Action) -> None: +def add_action_to_config_if_new( + raw_config: dict[str, Any], action: domain.Action +) -> None: # adds action to raw config if it is not defined yet. Existing action will be not # overwritten - tool_config = add_or_get_dict_key_value(raw_config, 'tool', {}) - finecode_config = add_or_get_dict_key_value(tool_config, 'finecode', {}) - action_config = add_or_get_dict_key_value(finecode_config, 'action', {}) + tool_config = add_or_get_dict_key_value(raw_config, "tool", {}) + finecode_config = add_or_get_dict_key_value(tool_config, "finecode", {}) + action_config = add_or_get_dict_key_value(finecode_config, "action", {}) if action.name not in action_config: action_raw_dict = { "source": action.source, - "handlers": [handler_to_dict(handler) for handler in action.handlers] + "handlers": [handler_to_dict(handler) for handler in action.handlers], } action_config[action.name] = action_raw_dict @@ -435,13 +485,15 @@ def add_action_to_config_if_new(raw_config: dict[str, Any], action: domain.Actio # ] -def add_or_get_dict_key_value(dict_obj: dict[str, Any], key: str, default_value: Any) -> Any: +def add_or_get_dict_key_value( + dict_obj: dict[str, Any], key: str, default_value: Any +) -> Any: if key not in dict_obj: value = default_value dict_obj[key] = value else: value = dict_obj[key] - + return value @@ -450,38 +502,38 @@ def handler_to_dict(handler: domain.ActionHandler) -> dict[str, str | list[str]] "name": handler.name, "source": handler.source, "env": handler.env, - "dependencies": handler.dependencies + "dependencies": handler.dependencies, } def add_runtime_dependency_group_if_new(project_config: dict[str, Any]) -> None: - runtime_dependencies = project_config.get('project', {}).get('dependencies', []) - - deps_groups = add_or_get_dict_key_value(project_config, 'dependency-groups', {}) - if 'runtime' not in deps_groups: - deps_groups['runtime'] = runtime_dependencies + runtime_dependencies = project_config.get("project", {}).get("dependencies", []) + + deps_groups = add_or_get_dict_key_value(project_config, "dependency-groups", {}) + if "runtime" not in deps_groups: + deps_groups["runtime"] = runtime_dependencies def merge_handlers_dependencies_into_groups(project_config: dict[str, Any]) -> None: # tool.finecode.action..handlers[x].dependencies - actions_dict = project_config.get('tool', {}).get('finecode', {}).get('action', {}) - if 'dependency-groups' not in project_config: - project_config['dependency-groups'] = {} - deps_groups = project_config['dependency-groups'] - + actions_dict = project_config.get("tool", {}).get("finecode", {}).get("action", {}) + if "dependency-groups" not in project_config: + project_config["dependency-groups"] = {} + deps_groups = project_config["dependency-groups"] + for action_info in actions_dict.values(): - action_handlers = action_info.get('handlers', []) - + action_handlers = action_info.get("handlers", []) + for handler in action_handlers: - handler_env = handler.get('env', None) + handler_env = handler.get("env", None) if handler_env is None: - logger.warning(f'Handler {handler} has no env, skip it') + logger.warning(f"Handler {handler} has no env, skip it") continue - deps = handler.get('dependencies', []) - + deps = handler.get("dependencies", []) + if handler_env not in deps_groups: deps_groups[handler_env] = [] - + env_deps = deps_groups[handler_env] # should we remove duplicates here? env_deps += deps @@ -504,15 +556,15 @@ def merge_handlers_dependencies_into_groups(project_config: dict[str, Any]) -> N def add_extension_runner_to_dependencies(project_config: dict[str, Any]) -> None: try: - deps_groups = project_config['dependency-groups'] + deps_groups = project_config["dependency-groups"] except KeyError: return - finecode_version = metadata.version('finecode') - + finecode_version = metadata.version("finecode") + for group_name, group_packages in deps_groups.items(): - if group_name == 'dev_workspace': + if group_name == "dev_workspace": # skip `dev_workspace` because it contains finecode already continue - group_packages.append(f'finecode_extension_runner == {finecode_version}') + group_packages.append(f"finecode_extension_runner == {finecode_version}") diff --git a/src/finecode/domain.py b/src/finecode/domain.py index 7518260..4229e93 100644 --- a/src/finecode/domain.py +++ b/src/finecode/domain.py @@ -13,7 +13,14 @@ def __init__(self, source: str) -> None: class ActionHandler: - def __init__(self, name: str, source: str, config: dict[str, typing.Any], env: str, dependencies: list[str]): + def __init__( + self, + name: str, + source: str, + config: dict[str, typing.Any], + env: str, + dependencies: list[str], + ): self.name: str = name self.source: str = source self.config: dict[str, typing.Any] = config @@ -43,7 +50,7 @@ def __init__( def_path: Path, status: ProjectStatus, env_configs: dict[str, EnvConfig], - actions: list[Action] | None = None + actions: list[Action] | None = None, ) -> None: self.name = name self.dir_path = dir_path @@ -66,17 +73,17 @@ def __str__(self) -> str: def __repr__(self) -> str: return str(self) - + @property def envs(self) -> list[str]: if self.actions is None: raise ValueError("Actions are not collected yet") - + all_envs_set = ordered_set.OrderedSet([]) for action in self.actions: action_envs = [handler.env for handler in action.handlers] all_envs_set |= ordered_set.OrderedSet(action_envs) - + return list(all_envs_set) @@ -135,5 +142,5 @@ class PartialResult(typing.NamedTuple): "Project", "TextDocumentInfo", "RunnerConfig", - "EnvConfig" + "EnvConfig", ] diff --git a/src/finecode/logger_utils.py b/src/finecode/logger_utils.py index 090c5ba..19dc4c9 100644 --- a/src/finecode/logger_utils.py +++ b/src/finecode/logger_utils.py @@ -4,8 +4,8 @@ from loguru import logger -from finecode_extension_runner import logs from finecode import app_dirs +from finecode_extension_runner import logs def init_logger(trace: bool, stdout: bool = False): diff --git a/src/finecode/lsp_server/endpoints/action_tree.py b/src/finecode/lsp_server/endpoints/action_tree.py index 70e477b..ec0de72 100644 --- a/src/finecode/lsp_server/endpoints/action_tree.py +++ b/src/finecode/lsp_server/endpoints/action_tree.py @@ -1,8 +1,8 @@ import asyncio from pathlib import Path -from loguru import logger import ordered_set +from loguru import logger from pygls.lsp.server import LanguageServer from finecode import context, domain @@ -179,7 +179,7 @@ async def __list_actions( all_started_coros = [] for envs in ws_context.ws_projects_extension_runners.values(): # all presets are expected to be in `dev_no_runtime` env - dev_no_runtime_runner = envs['dev_no_runtime'] + dev_no_runtime_runner = envs["dev_no_runtime"] all_started_coros.append(dev_no_runtime_runner.initialized_event.wait()) await asyncio.gather(*all_started_coros) @@ -281,15 +281,21 @@ async def __reload_action(action_node_id: str) -> None: action_name = splitted_action_id[1] try: - action = next(action for action in project.actions if action.name == action_name) + action = next( + action for action in project.actions if action.name == action_name + ) except StopIteration as error: logger.error(f"Unexpected error, project or action not found: {error}") raise InternalError() - all_handlers_envs = ordered_set.OrderedSet([handler.env for handler in action.handlers]) + all_handlers_envs = ordered_set.OrderedSet( + [handler.env for handler in action.handlers] + ) for env in all_handlers_envs: # parallel to speed up? - runner = global_state.ws_context.ws_projects_extension_runners[project_path][env] + runner = global_state.ws_context.ws_projects_extension_runners[project_path][ + env + ] try: await runner_client.reload_action(runner, action_name) diff --git a/src/finecode/lsp_server/endpoints/diagnostics.py b/src/finecode/lsp_server/endpoints/diagnostics.py index 4239c45..8005807 100644 --- a/src/finecode/lsp_server/endpoints/diagnostics.py +++ b/src/finecode/lsp_server/endpoints/diagnostics.py @@ -9,8 +9,14 @@ from loguru import logger from lsprotocol import types -from finecode import pygls_types_utils -from finecode import domain, project_analyzer, proxy_utils, services, context +from finecode import ( + context, + domain, + project_analyzer, + proxy_utils, + pygls_types_utils, + services, +) from finecode.lsp_server import global_state from finecode_extension_api.actions import lint as lint_action @@ -236,7 +242,7 @@ async def run_workspace_diagnostic_with_partial_results( params=exec_info.request_data, partial_result_token=partial_result_token, project_dir_path=exec_info.project_dir_path, - ws_context=global_state.ws_context + ws_context=global_state.ws_context, ) as response: async for partial_response in response: lint_subresult = lint_action.LintRunResult(**partial_response) @@ -281,14 +287,22 @@ async def workspace_diagnostic_with_partial_results( return types.WorkspaceDiagnosticReport(items=[]) -async def workspace_diagnostic_with_full_result(exec_infos: list[LintActionExecInfo], ws_context: context.WorkspaceContext): +async def workspace_diagnostic_with_full_result( + exec_infos: list[LintActionExecInfo], ws_context: context.WorkspaceContext +): send_tasks: list[asyncio.Task] = [] try: async with asyncio.TaskGroup() as tg: for exec_info in exec_infos: project = ws_context.ws_projects[exec_info.project_dir_path] task = tg.create_task( - services.run_action(action_name=exec_info.action_name, params=exec_info.request_data, project_def=project, ws_context=ws_context, preprocess_payload=False) + services.run_action( + action_name=exec_info.action_name, + params=exec_info.request_data, + project_def=project, + ws_context=ws_context, + preprocess_payload=False, + ) ) send_tasks.append(task) except ExceptionGroup as eg: @@ -368,7 +382,9 @@ async def _workspace_diagnostic( exec_infos=exec_infos, partial_result_token=params.partial_result_token ) else: - return await workspace_diagnostic_with_full_result(exec_infos=exec_infos, ws_context=global_state.ws_context) + return await workspace_diagnostic_with_full_result( + exec_infos=exec_infos, ws_context=global_state.ws_context + ) async def workspace_diagnostic( diff --git a/src/finecode/lsp_server/endpoints/document_sync.py b/src/finecode/lsp_server/endpoints/document_sync.py index b05a279..7cc47ff 100644 --- a/src/finecode/lsp_server/endpoints/document_sync.py +++ b/src/finecode/lsp_server/endpoints/document_sync.py @@ -75,7 +75,9 @@ async def document_did_close( ] for runner in runners_by_env.values(): if runner.status != runner_info.RunnerStatus.RUNNING: - logger.trace(f"Runner {runner.working_dir_path} is not running, skip it") + logger.trace( + f"Runner {runner.working_dir_path} is not running, skip it" + ) continue tg.create_task( diff --git a/src/finecode/lsp_server/endpoints/formatting.py b/src/finecode/lsp_server/endpoints/formatting.py index c2c207e..b6c2fee 100644 --- a/src/finecode/lsp_server/endpoints/formatting.py +++ b/src/finecode/lsp_server/endpoints/formatting.py @@ -5,8 +5,7 @@ from loguru import logger from lsprotocol import types -from finecode import pygls_types_utils -from finecode import proxy_utils +from finecode import proxy_utils, pygls_types_utils from finecode.lsp_server import global_state if TYPE_CHECKING: diff --git a/src/finecode/lsp_server/endpoints/inlay_hints.py b/src/finecode/lsp_server/endpoints/inlay_hints.py index 50e3382..8fa3a95 100644 --- a/src/finecode/lsp_server/endpoints/inlay_hints.py +++ b/src/finecode/lsp_server/endpoints/inlay_hints.py @@ -5,8 +5,7 @@ from loguru import logger from lsprotocol import types -from finecode import pygls_types_utils -from finecode import find_project, proxy_utils +from finecode import find_project, proxy_utils, pygls_types_utils from finecode.lsp_server import global_state if TYPE_CHECKING: diff --git a/src/finecode/lsp_server/lsp_server.py b/src/finecode/lsp_server/lsp_server.py index 0408070..751e255 100644 --- a/src/finecode/lsp_server/lsp_server.py +++ b/src/finecode/lsp_server/lsp_server.py @@ -9,27 +9,13 @@ from finecode import services as wm_services from finecode.lsp_server import global_state, schemas, services -from finecode.lsp_server.endpoints import ( - action_tree as action_tree_endpoints, -) -from finecode.lsp_server.endpoints import ( - code_actions as code_actions_endpoints, -) -from finecode.lsp_server.endpoints import ( - code_lens as code_lens_endpoints, -) -from finecode.lsp_server.endpoints import ( - diagnostics as diagnostics_endpoints, -) -from finecode.lsp_server.endpoints import ( - document_sync as document_sync_endpoints, -) -from finecode.lsp_server.endpoints import ( - formatting as formatting_endpoints, -) -from finecode.lsp_server.endpoints import ( - inlay_hints as inlay_hints_endpoints, -) +from finecode.lsp_server.endpoints import action_tree as action_tree_endpoints +from finecode.lsp_server.endpoints import code_actions as code_actions_endpoints +from finecode.lsp_server.endpoints import code_lens as code_lens_endpoints +from finecode.lsp_server.endpoints import diagnostics as diagnostics_endpoints +from finecode.lsp_server.endpoints import document_sync as document_sync_endpoints +from finecode.lsp_server.endpoints import formatting as formatting_endpoints +from finecode.lsp_server.endpoints import inlay_hints as inlay_hints_endpoints def create_lsp_server() -> LanguageServer: diff --git a/src/finecode/payload_preprocessor.py b/src/finecode/payload_preprocessor.py index a0ce625..a786a62 100644 --- a/src/finecode/payload_preprocessor.py +++ b/src/finecode/payload_preprocessor.py @@ -5,7 +5,9 @@ def preprocess_for_project( - action_name: str, payload: dict[str, typing.Any], project_dir_path: pathlib.Path, + action_name: str, + payload: dict[str, typing.Any], + project_dir_path: pathlib.Path, ws_context: context.WorkspaceContext, ) -> dict[str, typing.Any]: processed_payload = payload.copy() @@ -15,26 +17,38 @@ def preprocess_for_project( if action_name == "lint" or action_name == "format": if "file_paths" not in processed_payload: processed_payload["file_paths"] = None - + if action_name == "format" and "save" not in processed_payload: processed_payload["save"] = True elif action_name == "prepare_envs" or action_name == "prepare_runners": - runtime_venv_path = project_dir_path / '.venvs' / 'runtime' - project_def_path = project_dir_path / 'pyproject.toml' - envs = [{"name": "runtime", "venv_dir_path": runtime_venv_path, "project_def_path": project_def_path}] + runtime_venv_path = project_dir_path / ".venvs" / "runtime" + project_def_path = project_dir_path / "pyproject.toml" + envs = [ + { + "name": "runtime", + "venv_dir_path": runtime_venv_path, + "project_def_path": project_def_path, + } + ] # current approach: there are 4 default environments: runtime, dev_workspace, # dev, dev_no_runtime. `runtime` is created always, all other only if dependency # group for them exist. # In future there will be possibility to create additional envs and to configure # default ones. project_raw_config = ws_context.ws_projects_raw_configs[project_dir_path] - deps_groups = project_raw_config.get('dependency-groups', {}) + deps_groups = project_raw_config.get("dependency-groups", {}) # `dev_workspace` is handled separately in `prepare_env`, no need to include # here - for default_env in ['dev', 'dev_no_runtime']: + for default_env in ["dev", "dev_no_runtime"]: if default_env in deps_groups: - venv_path = project_dir_path / '.venvs' / default_env - envs.append({"name": default_env, "venv_dir_path": venv_path, "project_def_path": project_def_path }) + venv_path = project_dir_path / ".venvs" / default_env + envs.append( + { + "name": default_env, + "venv_dir_path": venv_path, + "project_def_path": project_def_path, + } + ) processed_payload["envs"] = envs for param, value in processed_payload.items(): diff --git a/src/finecode/project_analyzer.py b/src/finecode/project_analyzer.py index aad5d4a..5718062 100644 --- a/src/finecode/project_analyzer.py +++ b/src/finecode/project_analyzer.py @@ -7,9 +7,11 @@ def get_files_by_projects(projects_dirs_paths: list[Path]) -> dict[Path, list[Pa if len(projects_dirs_paths) == 1: project_dir = projects_dirs_paths[0] files_by_projects_dirs[project_dir] = [ - path for path in project_dir.rglob("*.py") + path + for path in project_dir.rglob("*.py") # TODO: make configurable? - if '__testdata__' not in path.relative_to(project_dir).parts and '.venvs' not in path.relative_to(project_dir).parts + if "__testdata__" not in path.relative_to(project_dir).parts + and ".venvs" not in path.relative_to(project_dir).parts ] else: # copy to avoid modifying of argument values @@ -55,9 +57,11 @@ def get_files_by_projects(projects_dirs_paths: list[Path]) -> dict[Path, list[Pa if len(dir_items_with_children) == 0: # if there are no children with subprojects, we can just rglob files_by_projects_dirs[project_dir_path].extend( - path for path in project_dir_path.rglob("*.py") + path + for path in project_dir_path.rglob("*.py") # TODO: make configurable? - if '__testdata__' not in path.relative_to(project_dir_path).parts and '.venvs' not in path.relative_to(project_dir_path).parts + if "__testdata__" not in path.relative_to(project_dir_path).parts + and ".venvs" not in path.relative_to(project_dir_path).parts ) else: # process all dir items which don't have child projects @@ -69,9 +73,13 @@ def get_files_by_projects(projects_dirs_paths: list[Path]) -> dict[Path, list[Pa files_by_projects_dirs[project_dir_path].append(dir_item) elif dir_item.is_dir(): files_by_projects_dirs[project_dir_path].extend( - path for path in dir_item.rglob("*.py") + path + for path in dir_item.rglob("*.py") # TODO: make configurable? - if '__testdata__' not in path.relative_to(project_dir_path).parts and '.venvs' not in path.relative_to(project_dir_path).parts + if "__testdata__" + not in path.relative_to(project_dir_path).parts + and ".venvs" + not in path.relative_to(project_dir_path).parts ) # process all dir items which have child projects @@ -108,9 +116,13 @@ def get_files_by_projects(projects_dirs_paths: list[Path]) -> dict[Path, list[Pa else: # subdirectory without child projects, rglob it files_by_projects_dirs[project_dir_path].extend( - path for path in dir_item.rglob("*.py") + path + for path in dir_item.rglob("*.py") # TODO: make configurable? - if '__testdata__' not in path.relative_to(project_dir_path).parts and '.venvs' not in path.relative_to(project_dir_path).parts + if "__testdata__" + not in path.relative_to(project_dir_path).parts + and ".venvs" + not in path.relative_to(project_dir_path).parts ) return files_by_projects_dirs diff --git a/src/finecode/proxy_utils.py b/src/finecode/proxy_utils.py index 65b902a..4dcf9d9 100644 --- a/src/finecode/proxy_utils.py +++ b/src/finecode/proxy_utils.py @@ -4,13 +4,13 @@ import pathlib from typing import Any -from loguru import logger import ordered_set +from loguru import logger from finecode import context, domain, find_project, services -from finecode.services import ActionRunFailed from finecode.runner import manager as runner_manager from finecode.runner import runner_client, runner_info +from finecode.services import ActionRunFailed def find_action_project( @@ -56,7 +56,13 @@ async def find_action_project_and_run( project = ws_context.ws_projects[project_path] try: - response = await services.run_action(action_name=action_name, params=params, project_def=project, ws_context=ws_context, preprocess_payload=False) + response = await services.run_action( + action_name=action_name, + params=params, + project_def=project, + ws_context=ws_context, + preprocess_payload=False, + ) except services.ActionRunFailed as exception: raise exception @@ -161,7 +167,7 @@ async def run_with_partial_results( params: dict[str, Any], partial_result_token: int | str, project_dir_path: pathlib.Path, - ws_context: context.WorkspaceContext + ws_context: context.WorkspaceContext, ) -> collections.abc.AsyncIterator[ collections.abc.AsyncIterable[domain.PartialResultRawValue] ]: @@ -176,8 +182,10 @@ async def run_with_partial_results( ) ) project = ws_context.ws_projects[project_dir_path] - action = next(action for action in project.actions if action.name == 'lint') - action_envs = ordered_set.OrderedSet([handler.env for handler in action.handlers]) + action = next(action for action in project.actions if action.name == "lint") + action_envs = ordered_set.OrderedSet( + [handler.env for handler in action.handlers] + ) runners_by_env = ws_context.ws_projects_extension_runners[project_dir_path] for env in action_envs: runner = runners_by_env[env] @@ -216,7 +224,7 @@ async def find_action_project_and_run_with_partial_results( params=params, partial_result_token=partial_result_token, project_dir_path=project_path, - ws_context=ws_context + ws_context=ws_context, ) @@ -256,5 +264,5 @@ def find_all_projects_with_action( "find_action_project_and_run_with_partial_results", "run_with_partial_results", # reexport for easier use of proxy helpers - "ActionRunFailed" + "ActionRunFailed", ] diff --git a/src/finecode/runner/manager.py b/src/finecode/runner/manager.py index 735218f..ee7e6e6 100644 --- a/src/finecode/runner/manager.py +++ b/src/finecode/runner/manager.py @@ -8,10 +8,9 @@ from loguru import logger from lsprotocol import types -from finecode import dirs_utils +from finecode import context, dirs_utils, domain, finecode_cmd +from finecode.config import collect_actions, config_models, read_configs from finecode.pygls_client_utils import create_lsp_client_io -from finecode import context, domain, finecode_cmd -from finecode.config import collect_actions, read_configs, config_models from finecode.runner import runner_client, runner_info from finecode.utils import iterable_subscribe @@ -69,9 +68,13 @@ async def start_extension_runner( runner_dir: Path, env_name: str, ws_context: context.WorkspaceContext ) -> runner_info.ExtensionRunnerInfo | None: runner_info_instance = runner_info.ExtensionRunnerInfo( - working_dir_path=runner_dir, env_name=env_name, status=runner_info.RunnerStatus.READY_TO_START, initialized_event=asyncio.Event(), client=None + working_dir_path=runner_dir, + env_name=env_name, + status=runner_info.RunnerStatus.READY_TO_START, + initialized_event=asyncio.Event(), + client=None, ) - + try: python_cmd = finecode_cmd.get_python_cmd(runner_dir, env_name) except ValueError: @@ -80,13 +83,15 @@ async def start_extension_runner( await notify_project_changed(ws_context.ws_projects[runner_dir]) except KeyError: ... - logger.error(f"Project {runner_dir} uses finecode, but env (venv) doesn't exist yet. Run `prepare_env` command to create it") + logger.error( + f"Project {runner_dir} uses finecode, but env (venv) doesn't exist yet. Run `prepare_env` command to create it" + ) return None process_args: list[str] = [ "--trace", f"--project-path={runner_dir.as_posix()}", - f"--env-name={env_name}" + f"--env-name={env_name}", ] env_config = ws_context.ws_projects[runner_dir].env_configs[env_name] runner_config = env_config.runner_config @@ -109,7 +114,7 @@ async def start_extension_runner( async def on_exit(): logger.debug(f"Extension Runner {runner_info_instance.working_dir_path} exited") runner_info_instance.status = runner_info.RunnerStatus.EXITED - await notify_project_changed(ws_context.ws_projects[runner_dir]) # TODO: fix + await notify_project_changed(ws_context.ws_projects[runner_dir]) # TODO: fix # TODO: restart if WM is not stopping runner_info_instance.client.server_exit_callback = on_exit @@ -134,11 +139,11 @@ async def on_progress(params: types.ProgressParams): register_progress_feature = runner_info_instance.client.feature(types.PROGRESS) register_progress_feature(on_progress) - + async def get_project_raw_config(params): # assume raw config exists, because if runner is running, there is always a # raw config - return { "config": json.dumps(ws_context.ws_projects_raw_configs[runner_dir]) } + return {"config": json.dumps(ws_context.ws_projects_raw_configs[runner_dir])} register_get_project_raw_config_feature = runner_info_instance.client.feature( "projects/getRawConfig" @@ -229,19 +234,30 @@ async def update_runners(ws_context: context.WorkspaceContext) -> None: for new_dir in new_dirs: project = ws_context.ws_projects[new_dir] project_status = project.status - if ws_context.ws_projects_extension_runners.get(new_dir, {}).get('dev_no_runtime', None) is not None: + if ( + ws_context.ws_projects_extension_runners.get(new_dir, {}).get( + "dev_no_runtime", None + ) + is not None + ): # start only if dev_no_runtime started successfully for env in project.envs: - if env == 'dev_no_runtime': + if env == "dev_no_runtime": # this env has already started above continue - runner_task = tg.create_task(start_extension_runner(runner_dir=new_dir, env_name=env, ws_context=ws_context)) + runner_task = tg.create_task( + start_extension_runner( + runner_dir=new_dir, env_name=env, ws_context=ws_context + ) + ) new_runners_tasks.append(runner_task) - + except ExceptionGroup as eg: for exception in eg.exceptions: - if isinstance(exception, runner_client.BaseRunnerRequestException) or isinstance(exception, RunnerFailedToStart): + if isinstance( + exception, runner_client.BaseRunnerRequestException + ) or isinstance(exception, RunnerFailedToStart): logger.error(exception.message) else: logger.error("Unexpected exception:") @@ -273,7 +289,9 @@ async def update_runners(ws_context: context.WorkspaceContext) -> None: raise RunnerFailedToStart("Failed to initialize runner") -async def start_runners_with_presets(projects: list[domain.Project], ws_context: context.WorkspaceContext) -> None: +async def start_runners_with_presets( + projects: list[domain.Project], ws_context: context.WorkspaceContext +) -> None: new_runners_tasks: list[asyncio.Task] = [] try: # first start runner in 'dev_no_runtime' env to be able to resolve presets for @@ -282,27 +300,45 @@ async def start_runners_with_presets(projects: list[domain.Project], ws_context: for project in projects: project_status = project.status if project_status == domain.ProjectStatus.CONFIG_VALID: - task = tg.create_task(_start_dev_no_runtime_runner(project_def=project, ws_context=ws_context)) + task = tg.create_task( + _start_dev_no_runtime_runner( + project_def=project, ws_context=ws_context + ) + ) new_runners_tasks.append(task) elif project_status != domain.ProjectStatus.NO_FINECODE: - raise RunnerFailedToStart(f"Project '{project.name}' has invalid configuration, status: {project_status.name}") + raise RunnerFailedToStart( + f"Project '{project.name}' has invalid configuration, status: {project_status.name}" + ) - save_runners_from_tasks_in_context(tasks=new_runners_tasks, ws_context=ws_context) + save_runners_from_tasks_in_context( + tasks=new_runners_tasks, ws_context=ws_context + ) except ExceptionGroup as eg: for exception in eg.exceptions: - if isinstance(exception, runner_client.BaseRunnerRequestException) or isinstance(exception, RunnerFailedToStart): + if isinstance( + exception, runner_client.BaseRunnerRequestException + ) or isinstance(exception, RunnerFailedToStart): logger.error(exception.message) else: logger.error("Unexpected exception:") logger.exception(exception) - raise RunnerFailedToStart("Failed to initialize runner(s). See previous logs for more details") + raise RunnerFailedToStart( + "Failed to initialize runner(s). See previous logs for more details" + ) -async def start_runner(project_def: domain.Project, env_name: str, ws_context: context.WorkspaceContext) -> runner_info.ExtensionRunnerInfo: - runner = await start_extension_runner(runner_dir=project_def.dir_path, env_name=env_name, ws_context=ws_context) - +async def start_runner( + project_def: domain.Project, env_name: str, ws_context: context.WorkspaceContext +) -> runner_info.ExtensionRunnerInfo: + runner = await start_extension_runner( + runner_dir=project_def.dir_path, env_name=env_name, ws_context=ws_context + ) + if runner is None: - raise RunnerFailedToStart(f"Runner '{env_name}' in project {project_def.name} failed to start") + raise RunnerFailedToStart( + f"Runner '{env_name}' in project {project_def.name} failed to start" + ) save_runner_in_context(runner=runner, ws_context=ws_context) @@ -311,14 +347,21 @@ async def start_runner(project_def: domain.Project, env_name: str, ws_context: c # because this requires resolved project config with presets await _init_lsp_client(runner=runner, project=project_def) - if project_def.dir_path not in ws_context.ws_projects_raw_configs or project_def.actions is None: + if ( + project_def.dir_path not in ws_context.ws_projects_raw_configs + or project_def.actions is None + ): try: - await read_configs.read_project_config(project=project_def, ws_context=ws_context) + await read_configs.read_project_config( + project=project_def, ws_context=ws_context + ) collect_actions.collect_actions( project_path=project_def.dir_path, ws_context=ws_context ) except config_models.ConfigurationError as exception: - raise RunnerFailedToStart(f"Found problem in configuration of {project_def.dir_path}: {exception.message}") + raise RunnerFailedToStart( + f"Found problem in configuration of {project_def.dir_path}: {exception.message}" + ) await update_runner_config(runner=runner, project=project_def) await _finish_runner_init(runner=runner, project=project_def, ws_context=ws_context) @@ -326,8 +369,12 @@ async def start_runner(project_def: domain.Project, env_name: str, ws_context: c return runner -async def _start_dev_no_runtime_runner(project_def: domain.Project, ws_context: context.WorkspaceContext) -> runner_info.ExtensionRunnerInfo: - return await start_runner(project_def=project_def, env_name='dev_no_runtime', ws_context=ws_context) +async def _start_dev_no_runtime_runner( + project_def: domain.Project, ws_context: context.WorkspaceContext +) -> runner_info.ExtensionRunnerInfo: + return await start_runner( + project_def=project_def, env_name="dev_no_runtime", ws_context=ws_context + ) async def _init_runner( @@ -338,14 +385,16 @@ async def _init_runner( # initialization is required to be able to perform other requests logger.trace(f"Init runner {runner.working_dir_path}") assert project.actions is not None - + await _init_lsp_client(runner=runner, project=project) await update_runner_config(runner=runner, project=project) await _finish_runner_init(runner=runner, project=project, ws_context=ws_context) -async def _init_lsp_client(runner: runner_info.ExtensionRunnerInfo, project: domain.Project) -> None: +async def _init_lsp_client( + runner: runner_info.ExtensionRunnerInfo, project: domain.Project +) -> None: try: await runner_client.initialize( runner, @@ -374,16 +423,22 @@ async def _init_lsp_client(runner: runner_info.ExtensionRunnerInfo, project: dom logger.debug("LSP Client initialized") -async def update_runner_config(runner: runner_info.ExtensionRunnerInfo, project: domain.Project) -> None: +async def update_runner_config( + runner: runner_info.ExtensionRunnerInfo, project: domain.Project +) -> None: assert project.actions is not None - config = runner_client.RunnerConfig(actions=project.actions, action_handler_configs=project.action_handler_configs) + config = runner_client.RunnerConfig( + actions=project.actions, action_handler_configs=project.action_handler_configs + ) try: await runner_client.update_config(runner, config) except runner_client.BaseRunnerRequestException as exception: runner.status = runner_info.RunnerStatus.FAILED await notify_project_changed(project) runner.initialized_event.set() - raise RunnerFailedToStart(f"Runner failed to update config: {exception.message}") + raise RunnerFailedToStart( + f"Runner failed to update config: {exception.message}" + ) logger.debug( f"Updated config of runner {runner.working_dir_path}," @@ -391,7 +446,11 @@ async def update_runner_config(runner: runner_info.ExtensionRunnerInfo, project: ) -async def _finish_runner_init(runner: runner_info.ExtensionRunnerInfo, project: domain.Project, ws_context: context.WorkspaceContext) -> None: +async def _finish_runner_init( + runner: runner_info.ExtensionRunnerInfo, + project: domain.Project, + ws_context: context.WorkspaceContext, +) -> None: runner.status = runner_info.RunnerStatus.RUNNING await notify_project_changed(project) @@ -402,7 +461,9 @@ async def _finish_runner_init(runner: runner_info.ExtensionRunnerInfo, project: runner.initialized_event.set() -def save_runners_from_tasks_in_context(tasks: list[asyncio.Task], ws_context: context.WorkspaceContext) -> None: +def save_runners_from_tasks_in_context( + tasks: list[asyncio.Task], ws_context: context.WorkspaceContext +) -> None: extension_runners: list[runner_info.ExtensionRunnerInfo] = [ runner.result() for runner in tasks if runner is not None ] @@ -411,11 +472,14 @@ def save_runners_from_tasks_in_context(tasks: list[asyncio.Task], ws_context: co save_runner_in_context(runner=new_runner, ws_context=ws_context) -def save_runner_in_context(runner: runner_info.ExtensionRunnerInfo, ws_context: context.WorkspaceContext) -> None: +def save_runner_in_context( + runner: runner_info.ExtensionRunnerInfo, ws_context: context.WorkspaceContext +) -> None: if runner.working_dir_path not in ws_context.ws_projects_extension_runners: ws_context.ws_projects_extension_runners[runner.working_dir_path] = {} - ws_context.ws_projects_extension_runners[runner.working_dir_path][runner.env_name] = runner - + ws_context.ws_projects_extension_runners[runner.working_dir_path][ + runner.env_name + ] = runner async def send_opened_files( @@ -454,7 +518,7 @@ async def check_runner(runner_dir: Path, env_name: str) -> bool: # get version of extension runner. If it works and we get valid # value, assume extension runner works correctly - cmd = f'{python_cmd} -m finecode_extension_runner.cli version' + cmd = f"{python_cmd} -m finecode_extension_runner.cli version" logger.debug(f"Run '{cmd}' in {runner_dir}") async_subprocess = await asyncio.create_subprocess_shell( cmd, @@ -471,15 +535,19 @@ async def check_runner(runner_dir: Path, env_name: str) -> bool: return False if async_subprocess.returncode != 0: - logger.debug(f"Return code: {async_subprocess.returncode}, stderr: {raw_stderr.decode()}") + logger.debug( + f"Return code: {async_subprocess.returncode}, stderr: {raw_stderr.decode()}" + ) return False - + stdout = raw_stdout.decode() - return 'FineCode Extension Runner ' in stdout + return "FineCode Extension Runner " in stdout def remove_runner_venv(runner_dir: Path, env_name: str) -> None: - venv_dir_path = finecode_cmd.get_venv_dir_path(project_path=runner_dir, env_name=env_name) + venv_dir_path = finecode_cmd.get_venv_dir_path( + project_path=runner_dir, env_name=env_name + ) if venv_dir_path.exists(): logger.debug(f"Remove venv {venv_dir_path}") shutil.rmtree(venv_dir_path) diff --git a/src/finecode/runner/runner_client.py b/src/finecode/runner/runner_client.py index 7e6fdae..52c5ef9 100644 --- a/src/finecode/runner/runner_client.py +++ b/src/finecode/runner/runner_client.py @@ -182,8 +182,8 @@ async def run_action( ), timeout=None, ) - - if hasattr(response, 'error'): + + if hasattr(response, "error"): raise ActionRunFailed(response.error) return_code = response.return_code @@ -200,7 +200,7 @@ async def run_action( else: raise Exception(f"Not support result format: {response.format}") - if response.status == 'stopped': + if response.status == "stopped": raise ActionRunStopped(message=result) return RunActionResponse(result=result, return_code=return_code) @@ -222,7 +222,9 @@ async def reload_action(runner: ExtensionRunnerInfo, action_name: str) -> None: ) -async def resolve_package_path(runner: ExtensionRunnerInfo, package_name: str) -> dict[str, str]: +async def resolve_package_path( + runner: ExtensionRunnerInfo, package_name: str +) -> dict[str, str]: # resolving package path is used directly after initialization of runner to get full # config, which is then registered in runner. In this time runner is not available # for any other actions, so `runner.started_event` stays not set and should not be @@ -247,10 +249,7 @@ class RunnerConfig: action_handler_configs: dict[str, dict[str, Any]] -async def update_config( - runner: ExtensionRunnerInfo, - config: RunnerConfig -) -> None: +async def update_config(runner: ExtensionRunnerInfo, config: RunnerConfig) -> None: await send_request( runner=runner, method=types.WORKSPACE_EXECUTE_COMMAND, diff --git a/src/finecode/services.py b/src/finecode/services.py index 074af6d..4e1c7f6 100644 --- a/src/finecode/services.py +++ b/src/finecode/services.py @@ -1,17 +1,12 @@ import pathlib import typing -from loguru import logger import ordered_set +from loguru import logger -from finecode import ( - context, - domain, - payload_preprocessor, - user_messages, -) -from finecode.runner import manager as runner_manager, runner_info -from finecode.runner import runner_client +from finecode import context, domain, payload_preprocessor, user_messages +from finecode.runner import manager as runner_manager +from finecode.runner import runner_client, runner_info async def restart_extension_runners( @@ -19,7 +14,9 @@ async def restart_extension_runners( ) -> None: # TODO: reload config? try: - runners_by_env = ws_context.ws_projects_extension_runners[runner_working_dir_path] + runners_by_env = ws_context.ws_projects_extension_runners[ + runner_working_dir_path + ] except KeyError: logger.error(f"Cannot find runner for {runner_working_dir_path}") return @@ -27,17 +24,21 @@ async def restart_extension_runners( new_runners_by_env: dict[str, runner_info.ExtensionRunnerInfo] = {} for runner in runners_by_env.values(): await runner_manager.stop_extension_runner(runner) - + new_runner = await runner_manager.start_extension_runner( - runner_dir=runner_working_dir_path, env_name=runner.env_name, ws_context=ws_context + runner_dir=runner_working_dir_path, + env_name=runner.env_name, + ws_context=ws_context, ) if new_runner is None: logger.error("Extension runner didn't start") continue new_runners_by_env[runner.env_name] = new_runner - ws_context.ws_projects_extension_runners[runner_working_dir_path] = new_runners_by_env - + ws_context.ws_projects_extension_runners[runner_working_dir_path] = ( + new_runners_by_env + ) + # parallel? for runner in new_runners_by_env.values(): await runner_manager._init_runner( @@ -48,7 +49,7 @@ async def restart_extension_runners( def on_shutdown(ws_context: context.WorkspaceContext): - + running_runners = [] for runners_by_env in ws_context.ws_projects_extension_runners.values(): for runner in runners_by_env.values(): @@ -59,7 +60,7 @@ def on_shutdown(ws_context: context.WorkspaceContext): for runner in running_runners: runner_manager.stop_extension_runner_sync(runner=runner) - + # TODO: stop MCP if running @@ -78,7 +79,7 @@ async def run_action( project_def: domain.Project, ws_context: context.WorkspaceContext, result_format: RunResultFormat = RunResultFormat.JSON, - preprocess_payload: bool = True + preprocess_payload: bool = True, ) -> RunActionResponse: formatted_params = str(params) if len(formatted_params) > 100: @@ -86,17 +87,21 @@ async def run_action( logger.trace(f"Execute action {action_name} with {formatted_params}") if project_def.status != domain.ProjectStatus.CONFIG_VALID: - raise ActionRunFailed(f"Project {project_def.dir_path} has no valid configuration and finecode." - " Please check logs.") + raise ActionRunFailed( + f"Project {project_def.dir_path} has no valid configuration and finecode." + " Please check logs." + ) if preprocess_payload: payload = payload_preprocessor.preprocess_for_project( - action_name=action_name, payload=params, project_dir_path=project_def.dir_path, - ws_context=ws_context + action_name=action_name, + payload=params, + project_dir_path=project_def.dir_path, + ws_context=ws_context, ) else: payload = params - + # cases: # - base: all action handlers are in one env # -> send `run_action` request to runner in env and let it handle concurrency etc. @@ -105,8 +110,12 @@ async def run_action( # -- concurrent execution of handlers # -- sequential execution of handlers assert project_def.actions is not None - action = next(action for action in project_def.actions if action.name == action_name) - all_handlers_envs = ordered_set.OrderedSet([handler.env for handler in action.handlers]) + action = next( + action for action in project_def.actions if action.name == action_name + ) + all_handlers_envs = ordered_set.OrderedSet( + [handler.env for handler in action.handlers] + ) all_handlers_are_in_one_env = len(all_handlers_envs) == 1 if all_handlers_are_in_one_env: @@ -117,11 +126,11 @@ async def run_action( env_name=env_name, project_def=project_def, ws_context=ws_context, - result_format=result_format + result_format=result_format, ) else: # TODO: concurrent vs sequential, this value should be taken from action config - run_concurrently = False # action_name == 'lint' + run_concurrently = False # action_name == 'lint' if run_concurrently: ... raise NotImplementedError() @@ -134,7 +143,7 @@ async def run_action( env_name=handler.env, project_def=project_def, ws_context=ws_context, - result_format=result_format + result_format=result_format, ) return response @@ -151,8 +160,10 @@ async def _run_action_in_env_runner( runners_by_env = ws_context.ws_projects_extension_runners[project_def.dir_path] runner = runners_by_env[env_name] if runner.status != runner_info.RunnerStatus.RUNNING: - raise ActionRunFailed(f"Runner {env_name} in project {project_def.dir_path} is not running. Status: {runner.status}") - + raise ActionRunFailed( + f"Runner {env_name} in project {project_def.dir_path} is not running. Status: {runner.status}" + ) + try: response = await runner_client.run_action( runner=runner, @@ -161,8 +172,14 @@ async def _run_action_in_env_runner( options={"result_format": result_format}, ) except runner_client.BaseRunnerRequestException as error: - runner_log_path = runner.working_dir_path / '.venvs' / runner.env_name / 'logs' / 'runner.log' - await user_messages.error(f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message} . Log file: {runner_log_path}") - raise ActionRunFailed(f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message} . Log file: {runner_log_path}") + runner_log_path = ( + runner.working_dir_path / ".venvs" / runner.env_name / "logs" / "runner.log" + ) + await user_messages.error( + f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message} . Log file: {runner_log_path}" + ) + raise ActionRunFailed( + f"Action {action_name} failed in {runner.env_name} of {runner.working_dir_path}: {error.message} . Log file: {runner_log_path}" + ) return response From 871f8c33f59c8c537ff9cd06f6a9225c8043b710 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Sun, 24 Aug 2025 13:51:21 +0200 Subject: [PATCH 14/46] Unify naming of attributes in action class --- .../src/finecode_extension_api/code_action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finecode_extension_api/src/finecode_extension_api/code_action.py b/finecode_extension_api/src/finecode_extension_api/code_action.py index a88fb28..e974628 100644 --- a/finecode_extension_api/src/finecode_extension_api/code_action.py +++ b/finecode_extension_api/src/finecode_extension_api/code_action.py @@ -89,7 +89,7 @@ class Action(Generic[RunPayloadType, RunContextType, RunResultType]): PAYLOAD_TYPE: typing.Type[RunActionPayload] = RunActionPayload RUN_CONTEXT_TYPE: typing.Type[RunActionContext] = RunActionContext RESULT_TYPE: typing.Type[RunActionResult] = RunActionResult - CONFIG: typing.Type[ActionConfig] = ActionConfig + CONFIG_TYPE: typing.Type[ActionConfig] = ActionConfig class StopActionRunWithResult(Exception): From b5de3a8778aaffef981dcd9cd7f2a45b39a16556 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Sun, 24 Aug 2025 18:45:04 +0200 Subject: [PATCH 15/46] Remove unused action handlers --- .../action_handlers/__init__.py | 5 - .../prepare_envs_dump_configs.py | 101 ------------------ .../prepare_runners_dump_configs.py | 74 ------------- 3 files changed, 180 deletions(-) delete mode 100644 finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py delete mode 100644 finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py index 555da63..7f3bdaf 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/__init__.py @@ -1,9 +1,7 @@ from .dump_config import DumpConfigHandler from .dump_config_save import DumpConfigSaveHandler -from .prepare_envs_dump_configs import PrepareEnvsDumpConfigsHandler from .prepare_envs_install_deps import PrepareEnvsInstallDepsHandler from .prepare_envs_read_configs import PrepareEnvsReadConfigsHandler -from .prepare_runners_dump_configs import PrepareRunnersDumpConfigsHandler from .prepare_runners_install_runner_and_presets import ( PrepareRunnersInstallRunnerAndPresetsHandler, ) @@ -11,11 +9,8 @@ __all__ = [ "DumpConfigHandler", - "PrepareEnvsDumpConfigsHandler", "PrepareEnvsInstallDepsHandler", "PrepareEnvsReadConfigsHandler", - # not used - "PrepareRunnersDumpConfigsHandler", "PrepareRunnersInstallRunnerAndPresetsHandler", "PrepareRunnersReadConfigsHandler", "DumpConfigSaveHandler", diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py deleted file mode 100644 index edab5fb..0000000 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_dump_configs.py +++ /dev/null @@ -1,101 +0,0 @@ -import dataclasses -import shutil - -from finecode_extension_api import code_action -from finecode_extension_api.actions import prepare_envs as prepare_envs_action -from finecode_extension_api.interfaces import ( - iactionrunner, - ilogger, - iprojectinfoprovider, -) - - -@dataclasses.dataclass -class PrepareEnvsDumpConfigsHandlerConfig(code_action.ActionHandlerConfig): ... - - -class PrepareEnvsDumpConfigsHandler( - code_action.ActionHandler[ - prepare_envs_action.PrepareEnvsAction, PrepareEnvsDumpConfigsHandlerConfig - ] -): - def __init__( - self, - action_runner: iactionrunner.IActionRunner, - project_info_provider: iprojectinfoprovider.IProjectInfoProvider, - logger: ilogger.ILogger, - ) -> None: - self.action_runner = action_runner - self.project_info_provider = project_info_provider - self.logger = logger - - async def run( - self, - payload: prepare_envs_action.PrepareEnvsRunPayload, - run_context: prepare_envs_action.PrepareEnvsRunContext, - ) -> prepare_envs_action.PrepareEnvsRunResult: - project_defs_pathes = set( - [env_info.project_def_path for env_info in payload.envs] - ) - if len(project_defs_pathes) != 1: - raise code_action.ActionFailedException( - "prepare_envs action currently supports only preparing environments in the same project where it is running(dump_configs handler)" - ) - - project_raw_config = await self.project_info_provider.get_project_raw_config() - - project_def_path = project_defs_pathes.pop() - project_dir_path = project_def_path.parent - # TODO: unify with call of dump_config in CLI - dump_dir_path = project_dir_path / "finecode_config_dump" - try: - dump_config_result = await self.action_runner.run_action( - name="dump_config", - payload={ - "source_file_path": project_def_path, - "project_raw_config": project_raw_config, - "target_file_path": dump_dir_path / "pyproject.toml", - }, - ) - new_project_def_path = dump_dir_path / "pyproject.toml" - for env_info in payload.envs: - run_context.project_def_path_by_venv_dir_path[ - env_info.venv_dir_path - ] = new_project_def_path - run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = ( - dump_config_result["config_dump"] - ) - except iactionrunner.BaseRunActionException as exception: - raise code_action.ActionFailedException( - f"Running 'dump_config' action as part of 'prepare_envs' failed: {type(exception)}, {exception.message}" - ) - - # after dumping config in another directory, pathes to project files like - # readme and source files are wrong. We cannot just change the pathes to the new - # one in project configuration, because they would be outside of the project(in - # parent directory) and pip doesn't support this. Instead, we create symlinks - # to all project files in the directory with dumped config. - # - readme is needed to avoid error that it is missing - # - source files are needed for runtime environment. During installation of - # requirements, pip automatically resolves symlinks and editable pathes point - # to original source files, not to temporary symlinks - # - # question: filemanager should be used here? - # for item in project_dir_path.iterdir(): - # if item.name == 'finecode_config_dump' or item.name == 'pyproject.toml': - # # ignore: - # # - dir with dumped config - # # - dumped config - # continue - - # new_item_path = dump_dir_path / item.name - # if new_item_path.exists(): - # if new_item_path.is_symlink(): - # new_item_path.unlink() - # elif new_item_path.is_dir(): - # shutil.rmtree(new_item_path) - # else: - # new_item_path.unlink() - # new_item_path.symlink_to(item, target_is_directory=item.is_dir()) - - return prepare_envs_action.PrepareEnvsRunResult(errors=[]) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py deleted file mode 100644 index 725b065..0000000 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_dump_configs.py +++ /dev/null @@ -1,74 +0,0 @@ -import dataclasses -import shutil - -from finecode_extension_api import code_action -from finecode_extension_api.actions import prepare_runners as prepare_runners_action -from finecode_extension_api.interfaces import ( - iactionrunner, - ilogger, - iprojectinfoprovider, -) - - -@dataclasses.dataclass -class PrepareRunnersDumpConfigsHandlerConfig(code_action.ActionHandlerConfig): ... - - -class PrepareRunnersDumpConfigsHandler( - code_action.ActionHandler[ - prepare_runners_action.PrepareRunnersAction, - PrepareRunnersDumpConfigsHandlerConfig, - ] -): - def __init__( - self, - action_runner: iactionrunner.IActionRunner, - project_info_provider: iprojectinfoprovider.IProjectInfoProvider, - logger: ilogger.ILogger, - ) -> None: - self.action_runner = action_runner - self.project_info_provider = project_info_provider - self.logger = logger - - async def run( - self, - payload: prepare_runners_action.PrepareRunnersRunPayload, - run_context: prepare_runners_action.PrepareRunnersRunContext, - ) -> prepare_runners_action.PrepareRunnersRunResult: - project_defs_pathes = set( - [env_info.project_def_path for env_info in payload.envs] - ) - if len(project_defs_pathes) != 1: - raise code_action.ActionFailedException( - "prepare_envs action currently supports only preparing environments in the same project where it is running(dump_configs handler)" - ) - - project_raw_config = await self.project_info_provider.get_project_raw_config() - - project_def_path = project_defs_pathes.pop() - project_dir_path = project_def_path.parent - # TODO: unify with call of dump_config in CLI - dump_dir_path = project_dir_path / "finecode_config_dump" - try: - dump_config_result = await self.action_runner.run_action( - name="dump_config", - payload={ - "source_file_path": project_def_path, - "project_raw_config": project_raw_config, - "target_file_path": dump_dir_path / "pyproject.toml", - }, - ) - new_project_def_path = dump_dir_path / "pyproject.toml" - for env_info in payload.envs: - run_context.project_def_path_by_venv_dir_path[ - env_info.venv_dir_path - ] = new_project_def_path - run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = ( - dump_config_result["config_dump"] - ) - except iactionrunner.BaseRunActionException as exception: - raise code_action.ActionFailedException( - f"Running 'dump_config' action as part of 'prepare_envs' failed: {type(exception)}, {exception.message}" - ) - - return prepare_runners_action.PrepareRunnersRunResult(errors=[]) From 2e1c9624d54d54a8381bf0c39f9f7eaedab86b0d Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Sun, 24 Aug 2025 21:12:58 +0200 Subject: [PATCH 16/46] Add error handling in preparing dev_workspace envs. Remove useless code in project info provider --- .../impls/project_info_provider.py | 4 ---- src/finecode/cli_app/prepare_envs.py | 8 ++++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py b/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py index 92e2f0e..ee1472f 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py +++ b/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py @@ -6,9 +6,5 @@ class ProjectInfoProvider(iprojectinfoprovider.IProjectInfoProvider): - def __init__( - self, - ) -> None: ... - async def get_project_raw_config(self) -> dict[str, Any]: return await project_raw_config_getter() diff --git a/src/finecode/cli_app/prepare_envs.py b/src/finecode/cli_app/prepare_envs.py index ebab8ce..7ee3b54 100644 --- a/src/finecode/cli_app/prepare_envs.py +++ b/src/finecode/cli_app/prepare_envs.py @@ -248,9 +248,8 @@ async def check_or_recreate_all_dev_workspace_envs( envs += invalid_envs - # TODO: check result try: - await services.run_action( + action_result = await services.run_action( action_name="prepare_dev_workspaces_envs", params={ "envs": envs, @@ -264,3 +263,8 @@ async def check_or_recreate_all_dev_workspace_envs( raise PrepareEnvsFailed( f"'prepare_dev_workspaces_env' failed in {current_project.name}: {exception.message}" ) + + if action_result.return_code != 0: + raise PrepareEnvsFailed( + f"'prepare_dev_workspaces_env' ended in {current_project.name} with return code {action_result.return_code}: {action_result.result}" + ) From 405b17c93f40469814ef2d785c0cb8c134863920 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Sun, 24 Aug 2025 21:21:15 +0200 Subject: [PATCH 17/46] Check whether action handler has source in collect_actions --- src/finecode/config/collect_actions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/finecode/config/collect_actions.py b/src/finecode/config/collect_actions.py index f43c013..69a7339 100644 --- a/src/finecode/config/collect_actions.py +++ b/src/finecode/config/collect_actions.py @@ -41,7 +41,9 @@ def _collect_action_handler_configs_in_config( action_handlers_configs = config["tool"]["finecode"].get("action_handler", []) action_handler_config_by_source: dict[str, dict[str, Any]] = {} for handler_def in action_handlers_configs: - # TODO: validate that source field exist? + if "source" not in handler_def or not isinstance(handler_def['source'], str): + raise config_models.ConfigurationError("Action handler definition expected to have a 'source' field(to identify handler) and it should be a string") + handler_config = handler_def.get("config", None) if handler_config is not None: action_handler_config_by_source[handler_def["source"]] = handler_config From af87d385538ee85e528aaf448ac38d98e2a5f8cc Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 07:06:35 +0200 Subject: [PATCH 18/46] Add finecode_config_dump to all gitignores --- .gitignore | 3 +-- extensions/fine_python_pip/.gitignore | 4 +++- extensions/fine_python_virtualenv/.gitignore | 3 ++- finecode_dev_common_preset/.gitignore | 11 +++++++++++ finecode_extension_api/.gitignore | 3 +-- finecode_extension_runner/.gitignore | 5 ++--- 6 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 finecode_dev_common_preset/.gitignore diff --git a/.gitignore b/.gitignore index 6a63dc4..3418202 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,5 @@ src/finecode/_version.py src/finecode.egg-info .venvs -finecode_dump_config/* -!finecode_dump_config/pyproject.toml +finecode_config_dump/ build/ \ No newline at end of file diff --git a/extensions/fine_python_pip/.gitignore b/extensions/fine_python_pip/.gitignore index 42b6063..d1dacee 100644 --- a/extensions/fine_python_pip/.gitignore +++ b/extensions/fine_python_pip/.gitignore @@ -1,2 +1,4 @@ .venvs -*.egg-info \ No newline at end of file +src/*.egg-info +__pycache__ +finecode_config_dump/ diff --git a/extensions/fine_python_virtualenv/.gitignore b/extensions/fine_python_virtualenv/.gitignore index 5a23f9f..250a6a2 100644 --- a/extensions/fine_python_virtualenv/.gitignore +++ b/extensions/fine_python_virtualenv/.gitignore @@ -1,4 +1,5 @@ .venvs build/ src/*.egg-info/ -__pycache__ \ No newline at end of file +__pycache__ +finecode_config_dump/ diff --git a/finecode_dev_common_preset/.gitignore b/finecode_dev_common_preset/.gitignore new file mode 100644 index 0000000..bf9e16a --- /dev/null +++ b/finecode_dev_common_preset/.gitignore @@ -0,0 +1,11 @@ +__pycache__ +.coverage +.pytest_cache +.mypy_cache +dist + +src/*.egg-info + +.venvs +finecode_config_dump/ +build/ \ No newline at end of file diff --git a/finecode_extension_api/.gitignore b/finecode_extension_api/.gitignore index ee1176b..81441a3 100644 --- a/finecode_extension_api/.gitignore +++ b/finecode_extension_api/.gitignore @@ -1,5 +1,4 @@ .venvs/ *.egg-info build/ -finecode_dump_config/* -!finecode_dump_config/pyproject.toml \ No newline at end of file +finecode_config_dump/ \ No newline at end of file diff --git a/finecode_extension_runner/.gitignore b/finecode_extension_runner/.gitignore index 37ce05e..47b43fe 100644 --- a/finecode_extension_runner/.gitignore +++ b/finecode_extension_runner/.gitignore @@ -6,9 +6,8 @@ __pycache__ dist src/finecode_extension_runner/_version.py -src/finecode_extension_runner.egg-info +src/*.egg-info .venvs -finecode_dump_config/* -!finecode_dump_config/pyproject.toml +finecode_config_dump/ build/ \ No newline at end of file From b93df1c4b04ac69897bacdfbd9473a760261a050 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 07:22:02 +0200 Subject: [PATCH 19/46] Remove poetry locks, they are not used anymore --- finecode_dev_common_preset/poetry.lock | 680 --------------- finecode_extension_api/poetry.lock | 902 -------------------- presets/fine_python_format/poetry.lock | 888 ------------------- presets/fine_python_lint/poetry.lock | 867 ------------------- presets/fine_python_recommended/poetry.lock | 858 ------------------- 5 files changed, 4195 deletions(-) delete mode 100644 finecode_dev_common_preset/poetry.lock delete mode 100644 finecode_extension_api/poetry.lock delete mode 100644 presets/fine_python_format/poetry.lock delete mode 100644 presets/fine_python_lint/poetry.lock delete mode 100644 presets/fine_python_recommended/poetry.lock diff --git a/finecode_dev_common_preset/poetry.lock b/finecode_dev_common_preset/poetry.lock deleted file mode 100644 index 0fd78ba..0000000 --- a/finecode_dev_common_preset/poetry.lock +++ /dev/null @@ -1,680 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "attrs" -version = "25.3.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, -] - -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] - -[[package]] -name = "black" -version = "25.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "click" -version = "8.2.1" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "platform_system == \"Windows\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "fine-python-aksem" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["main"] -files = [] -develop = false - -[package.dependencies] -fine_python_module_exports = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_module_exports"} - -[package.source] -type = "git" -url = "https://github.com/Aksem/fine_python_aksem.git" -reference = "HEAD" -resolved_reference = "56ce22c3fb1228a6e85d1a8727a0fa74787608d9" - -[[package]] -name = "fine-python-ast" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_ast-0.1.0-py3-none-any.whl", hash = "sha256:3dcdcdc40ed89e0b90686f2ad5d358d8edba622a5de4c1f52f2f0787dcc4e07d"}, - {file = "fine_python_ast-0.1.0.tar.gz", hash = "sha256:ebc68aef7d0379f8771f610e436f6075b7c299a8895f1b9b7769fd038f95397e"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" - -[[package]] -name = "fine-python-black" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_black-0.1.0-py3-none-any.whl", hash = "sha256:27a012123783e4217222546e37280660e5a4948cdd3aa12d7fae7a3ce21a875d"}, - {file = "fine_python_black-0.1.0.tar.gz", hash = "sha256:ce300e897b4d819abe4754177cd5b81d29b2d4d04233942135edc1a8ed234bf7"}, -] - -[package.dependencies] -black = ">=25.1.0,<26.0.0" -finecode_extension_api = "0.1.0" - -[[package]] -name = "fine-python-flake8" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_flake8-0.1.0-py3-none-any.whl", hash = "sha256:7ed7641505936f334396631f779953a13ae18c8f207a8258d722e50fa452d792"}, - {file = "fine_python_flake8-0.1.0.tar.gz", hash = "sha256:ccba9a1ec41f5f3aa756efa63bd64650237ad63d3098aedf673e9c254e502914"}, -] - -[package.dependencies] -fine_python_ast = "0.1.0" -finecode_extension_api = "0.1.0" -flake8 = ">=7.1.2,<8.0.0" -types-flake8 = ">=7.1.0.20241020,<8.0.0.0" - -[[package]] -name = "fine-python-format" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_format-0.1.0-py3-none-any.whl", hash = "sha256:26315bc8ae5a4830efd9ee41ab3e220beead52c0702cc6f82df720e59dbfbfc2"}, - {file = "fine_python_format-0.1.0.tar.gz", hash = "sha256:8d8031316abed4761d096c2a0c100c8d7cc6d391ea386149ef625964ab80d0d2"}, -] - -[package.dependencies] -fine_python_black = "0.1.0" -fine_python_isort = "0.1.0" - -[[package]] -name = "fine-python-isort" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_isort-0.1.0-py3-none-any.whl", hash = "sha256:cbd6cd5502d65122e9f6461758f78db8d9e5628ab97c41a67ee6ef85a3526c8e"}, - {file = "fine_python_isort-0.1.0.tar.gz", hash = "sha256:64468a96b49663226b422885b25de7c22c24f6dafef6c25c1d02ea6e49662b53"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" -isort = ">=5.13,<6" - -[[package]] -name = "fine-python-lint" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_lint-0.1.0-py3-none-any.whl", hash = "sha256:180517a54a38ab4942bf51611d03a9afd3668e8d11f59e289444c2e1756434de"}, - {file = "fine_python_lint-0.1.0.tar.gz", hash = "sha256:597660e54fe4fa4024b54f0050cdc378c48d0a03fd2938d1c0ae60204a8160d8"}, -] - -[package.dependencies] -fine_python_flake8 = "0.1.0" -fine_python_mypy = "0.1.0" -flake8-bugbear = ">=24.12.12,<25.0.0" - -[[package]] -name = "fine-python-module-exports" -version = "0.1.0" -description = "" -optional = false -python-versions = ">= 3.11, < 3.14" -groups = ["main"] -files = [] -develop = false - -[package.dependencies] -fine_python_ast = "0.1.0" -finecode_extension_api = "0.1.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "fa081a6f836fb6ca46611fc1d9561ba74ad16686" -subdirectory = "extensions/fine_python_module_exports" - -[[package]] -name = "fine-python-mypy" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["main"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = "0.1.0" -mypy = ">=1.15,<2.0" - -[package.source] -type = "directory" -url = "../extensions/fine_python_mypy" - -[[package]] -name = "fine-python-recommended" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_recommended-0.1.0-py3-none-any.whl", hash = "sha256:3416df1eb4af8ce80206684c87c55dc55331999ef8b9ffb60a874daf0ddfbb98"}, - {file = "fine_python_recommended-0.1.0.tar.gz", hash = "sha256:bc721e5eb381b02a6049f171e042c29cbbd9dac5ecec8c22a88806a6811d3b9d"}, -] - -[package.dependencies] -fine_python_format = "0.1.0" -fine_python_lint = "0.1.0" - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "flake8" -version = "7.2.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, - {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.13.0,<2.14.0" -pyflakes = ">=3.3.0,<3.4.0" - -[[package]] -name = "flake8-bugbear" -version = "24.12.12" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" -groups = ["main"] -files = [ - {file = "flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e"}, - {file = "flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -flake8 = ">=6.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -groups = ["main"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy" -version = "1.15.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pycodestyle" -version = "2.13.0" -description = "Python style guide checker" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, - {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, -] - -[[package]] -name = "pydantic" -version = "2.11.5" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7"}, - {file = "pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.33.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pyflakes" -version = "3.3.2" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, - {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, -] - -[[package]] -name = "types-flake8" -version = "7.2.0.20250330" -description = "Typing stubs for flake8" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "types_flake8-7.2.0.20250330-py3-none-any.whl", hash = "sha256:af31590a269586309b80a439c94e59359e139e17475a8fc8d3c426ab01a40547"}, - {file = "types_flake8-7.2.0.20250330.tar.gz", hash = "sha256:481e5c914a26fabd23e85704ad055f2716f9238740bfe6fe77259c2533cb970c"}, -] - -[package.dependencies] -types-pyflakes = "*" - -[[package]] -name = "types-pyflakes" -version = "3.3.2.20250511" -description = "Typing stubs for pyflakes" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "types_pyflakes-3.3.2.20250511-py3-none-any.whl", hash = "sha256:85802fdd0b64d3553ef12ac0ba02d85c4bbd38747579c544e6bb005ec455becf"}, - {file = "types_pyflakes-3.3.2.20250511.tar.gz", hash = "sha256:d0ef58f9ec15eab2a9e427814f48587be4eb2752a8ae7dec201d65086f50ace2"}, -] - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "typing-inspection" -version = "0.4.1" -description = "Runtime typing introspection tools" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, - {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, -] - -[package.dependencies] -typing-extensions = ">=4.12.0" - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "986f7e2322de03c2f24845d27748b9a3cb4bab269efe8e91ba627714b92e8dfc" diff --git a/finecode_extension_api/poetry.lock b/finecode_extension_api/poetry.lock deleted file mode 100644 index a6c4ecc..0000000 --- a/finecode_extension_api/poetry.lock +++ /dev/null @@ -1,902 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "attrs" -version = "25.3.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, -] - -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] - -[[package]] -name = "black" -version = "25.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "cattrs" -version = "24.1.3" -description = "Composable complex class support for attrs and dataclasses." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5"}, - {file = "cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff"}, -] - -[package.dependencies] -attrs = ">=23.1.0" - -[package.extras] -bson = ["pymongo (>=4.4.0)"] -cbor2 = ["cbor2 (>=5.4.6)"] -msgpack = ["msgpack (>=1.0.5)"] -msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] -orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] -pyyaml = ["pyyaml (>=6.0)"] -tomlkit = ["tomlkit (>=0.11.8)"] -ujson = ["ujson (>=5.7.0)"] - -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] -markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "fine-python-aksem" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_module_exports = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_module_exports"} - -[package.source] -type = "git" -url = "https://github.com/Aksem/fine_python_aksem.git" -reference = "HEAD" -resolved_reference = "56ce22c3fb1228a6e85d1a8727a0fa74787608d9" - -[[package]] -name = "fine-python-ast" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" -subdirectory = "extensions/fine_python_ast" - -[[package]] -name = "fine-python-black" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -black = ">=25.1.0,<26.0.0" -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" -subdirectory = "extensions/fine_python_black" - -[[package]] -name = "fine-python-flake8" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_ast = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_ast"} -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} -flake8 = ">=7.1.2,<8.0.0" -types-flake8 = ">=7.1.0.20241020,<8.0.0.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" -subdirectory = "extensions/fine_python_flake8" - -[[package]] -name = "fine-python-format" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_black = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_black"} -fine_python_isort = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_isort"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" -subdirectory = "presets/fine_python_format" - -[[package]] -name = "fine-python-isort" -version = "0.1.0" -description = "" -optional = false -python-versions = ">= 3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} -isort = ">=5.13,<6" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" -subdirectory = "extensions/fine_python_isort" - -[[package]] -name = "fine-python-lint" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_flake8 = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_flake8"} -fine_python_mypy = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_mypy"} -flake8-bugbear = ">=24.12.12,<25.0.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" -subdirectory = "presets/fine_python_lint" - -[[package]] -name = "fine-python-module-exports" -version = "0.1.0" -description = "" -optional = false -python-versions = ">= 3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_ast = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_ast"} -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" -subdirectory = "extensions/fine_python_module_exports" - -[[package]] -name = "fine-python-mypy" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} -mypy = ">=1.15,<2.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" -subdirectory = "extensions/fine_python_mypy" - -[[package]] -name = "fine-python-recommended" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_format = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_format"} -fine_python_lint = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_lint"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" -subdirectory = "presets/fine_python_recommended" - -[[package]] -name = "finecode" -version = "0.2.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -click = "==8.1.*" -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} -loguru = "==0.7.*" -ordered-set = "==4.1.*" -platformdirs = "==4.3.*" -pydantic = "==2.10.*" -pygls = "2.0.0-a2" -tomlkit = "==0.11.*" -watchdog = "==4.0.*" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "51fa7a9bb80d21455dc89c8b1c6e7049177363da" - -[[package]] -name = "finecode-dev-common-preset" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = true - -[package.dependencies] -fine_python_aksem = {git = "https://github.com/Aksem/fine_python_aksem.git"} -fine_python_recommended = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_recommended"} - -[package.source] -type = "directory" -url = "../finecode_dev_common_preset" - -[[package]] -name = "flake8" -version = "7.2.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, - {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.13.0,<2.14.0" -pyflakes = ">=3.3.0,<3.4.0" - -[[package]] -name = "flake8-bugbear" -version = "24.12.12" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" -groups = ["dev"] -files = [ - {file = "flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e"}, - {file = "flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -flake8 = ">=6.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -groups = ["dev"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "loguru" -version = "0.7.3" -description = "Python logging made (stupidly) simple" -optional = false -python-versions = "<4.0,>=3.5" -groups = ["dev"] -files = [ - {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, - {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} -win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} - -[package.extras] -dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""] - -[[package]] -name = "lsprotocol" -version = "2024.0.0b1" -description = "Python types for Language Server Protocol." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "lsprotocol-2024.0.0b1-py3-none-any.whl", hash = "sha256:93785050ac155ae2be16b1ebfbd74c214feb3d3ef77b10399ce941e5ccef6ebd"}, - {file = "lsprotocol-2024.0.0b1.tar.gz", hash = "sha256:d3667fb70894d361aa6c495c5c8a1b2e6a44be65ff84c21a9cbb67ebfb4830fd"}, -] - -[package.dependencies] -attrs = ">=21.3.0" -cattrs = "!=23.2.1" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy" -version = "1.15.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "ordered-set" -version = "4.1.0" -description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, - {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, -] - -[package.extras] -dev = ["black", "mypy", "pytest"] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.7" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, - {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pycodestyle" -version = "2.13.0" -description = "Python style guide checker" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, - {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, -] - -[[package]] -name = "pydantic" -version = "2.10.6" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, - {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" -typing-extensions = ">=4.12.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.27.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pyflakes" -version = "3.3.2" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, - {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, -] - -[[package]] -name = "pygls" -version = "2.0.0a2" -description = "A pythonic generic language server (pronounced like 'pie glass')" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pygls-2.0.0a2-py3-none-any.whl", hash = "sha256:b202369321409343aa6440d73111d9fa0c22e580466ff1c7696b8358bb91f243"}, - {file = "pygls-2.0.0a2.tar.gz", hash = "sha256:03e00634ed8d989918268aaa4b4a0c3ab857ea2d4ee94514a52efa5ddd6d5d9f"}, -] - -[package.dependencies] -cattrs = ">=23.1.2" -lsprotocol = "2024.0.0b1" - -[package.extras] -ws = ["websockets (>=13.0)"] - -[[package]] -name = "tomlkit" -version = "0.11.8" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, - {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, -] - -[[package]] -name = "types-flake8" -version = "7.2.0.20250330" -description = "Typing stubs for flake8" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "types_flake8-7.2.0.20250330-py3-none-any.whl", hash = "sha256:af31590a269586309b80a439c94e59359e139e17475a8fc8d3c426ab01a40547"}, - {file = "types_flake8-7.2.0.20250330.tar.gz", hash = "sha256:481e5c914a26fabd23e85704ad055f2716f9238740bfe6fe77259c2533cb970c"}, -] - -[package.dependencies] -types-pyflakes = "*" - -[[package]] -name = "types-pyflakes" -version = "3.3.2.20250429" -description = "Typing stubs for pyflakes" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "types_pyflakes-3.3.2.20250429-py3-none-any.whl", hash = "sha256:f9ccc1968ddd1a18232c1e66cfcce8a9e8f4b2b85fbbf682bf87148a2b2d58a0"}, - {file = "types_pyflakes-3.3.2.20250429.tar.gz", hash = "sha256:a81b0ee91e34d143f655d366bd4002730f0e342a5aa338779d2f995515ce1c5c"}, -] - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "watchdog" -version = "4.0.2" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"}, - {file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"}, - {file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"}, - {file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"}, - {file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -description = "A small Python utility to set file creation time on Windows" -optional = false -python-versions = ">=3.5" -groups = ["dev"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, - {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, -] - -[package.extras] -dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "ca4aedcc1b712706890117d8c59ed135f4fc11ee925c6237e126f8d891ee959c" diff --git a/presets/fine_python_format/poetry.lock b/presets/fine_python_format/poetry.lock deleted file mode 100644 index 0551df1..0000000 --- a/presets/fine_python_format/poetry.lock +++ /dev/null @@ -1,888 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "attrs" -version = "25.3.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, -] - -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] - -[[package]] -name = "black" -version = "25.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "cattrs" -version = "24.1.3" -description = "Composable complex class support for attrs and dataclasses." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5"}, - {file = "cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff"}, -] - -[package.dependencies] -attrs = ">=23.1.0" - -[package.extras] -bson = ["pymongo (>=4.4.0)"] -cbor2 = ["cbor2 (>=5.4.6)"] -msgpack = ["msgpack (>=1.0.5)"] -msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] -orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] -pyyaml = ["pyyaml (>=6.0)"] -tomlkit = ["tomlkit (>=0.11.8)"] -ujson = ["ujson (>=5.7.0)"] - -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev"] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} - -[[package]] -name = "fine-python-aksem" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_module_exports = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_module_exports"} - -[package.source] -type = "git" -url = "https://github.com/Aksem/fine_python_aksem.git" -reference = "HEAD" -resolved_reference = "56ce22c3fb1228a6e85d1a8727a0fa74787608d9" - -[[package]] -name = "fine-python-ast" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = "0.1.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "f0b0757ea2a6dcd5ce8db13bba72dd04d404fa5b" -subdirectory = "extensions/fine_python_ast" - -[[package]] -name = "fine-python-black" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_black-0.1.0-py3-none-any.whl", hash = "sha256:27a012123783e4217222546e37280660e5a4948cdd3aa12d7fae7a3ce21a875d"}, - {file = "fine_python_black-0.1.0.tar.gz", hash = "sha256:ce300e897b4d819abe4754177cd5b81d29b2d4d04233942135edc1a8ed234bf7"}, -] - -[package.dependencies] -black = ">=25.1.0,<26.0.0" -finecode_extension_api = "0.1.0" - -[[package]] -name = "fine-python-flake8" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_ast = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_ast"} -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} -flake8 = ">=7.1.2,<8.0.0" -types-flake8 = ">=7.1.0.20241020,<8.0.0.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "f0b0757ea2a6dcd5ce8db13bba72dd04d404fa5b" -subdirectory = "extensions/fine_python_flake8" - -[[package]] -name = "fine-python-isort" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_isort-0.1.0-py3-none-any.whl", hash = "sha256:cbd6cd5502d65122e9f6461758f78db8d9e5628ab97c41a67ee6ef85a3526c8e"}, - {file = "fine_python_isort-0.1.0.tar.gz", hash = "sha256:64468a96b49663226b422885b25de7c22c24f6dafef6c25c1d02ea6e49662b53"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" -isort = ">=5.13,<6" - -[[package]] -name = "fine-python-lint" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_flake8 = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_flake8"} -fine_python_mypy = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_mypy"} -flake8-bugbear = ">=24.12.12,<25.0.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "f0b0757ea2a6dcd5ce8db13bba72dd04d404fa5b" -subdirectory = "presets/fine_python_lint" - -[[package]] -name = "fine-python-module-exports" -version = "0.1.0" -description = "" -optional = false -python-versions = ">= 3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_ast = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_ast"} -finecode_extension_api = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "finecode_extension_api"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "f0b0757ea2a6dcd5ce8db13bba72dd04d404fa5b" -subdirectory = "extensions/fine_python_module_exports" - -[[package]] -name = "fine-python-mypy" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -finecode_extension_api = "0.1.0" -mypy = ">=1.15,<2.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "f0b0757ea2a6dcd5ce8db13bba72dd04d404fa5b" -subdirectory = "extensions/fine_python_mypy" - -[[package]] -name = "fine-python-recommended" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_format = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_format"} -fine_python_lint = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_lint"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "f0b0757ea2a6dcd5ce8db13bba72dd04d404fa5b" -subdirectory = "presets/fine_python_recommended" - -[[package]] -name = "finecode" -version = "0.2.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["dev"] -files = [ - {file = "finecode-0.2.0-py3-none-any.whl", hash = "sha256:42e4980a219a91e9c8a84e51d5df3f7c43d160230bb0fb6001a3e4f28a42041c"}, - {file = "finecode-0.2.0.tar.gz", hash = "sha256:149001825d27403e18c935f67b710184cea3cf3b846283996a595fabcc41d67e"}, -] - -[package.dependencies] -click = "==8.1.*" -finecode_extension_api = "0.1.0" -loguru = "==0.7.*" -ordered-set = "==4.1.*" -platformdirs = "==4.3.*" -pydantic = "==2.10.*" -pygls = "2.0.0-a2" -tomlkit = "==0.11.*" -watchdog = "==4.0.*" - -[[package]] -name = "finecode-dev-common-preset" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = true - -[package.dependencies] -fine_python_aksem = {git = "https://github.com/Aksem/fine_python_aksem.git"} -fine_python_recommended = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_recommended"} - -[package.source] -type = "directory" -url = "../../finecode_dev_common_preset" - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["main", "dev"] -files = [] -develop = false - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "f0b0757ea2a6dcd5ce8db13bba72dd04d404fa5b" -subdirectory = "finecode_extension_api" - -[[package]] -name = "flake8" -version = "7.2.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, - {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.13.0,<2.14.0" -pyflakes = ">=3.3.0,<3.4.0" - -[[package]] -name = "flake8-bugbear" -version = "24.12.12" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" -groups = ["dev"] -files = [ - {file = "flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e"}, - {file = "flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -flake8 = ">=6.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -groups = ["main"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "loguru" -version = "0.7.3" -description = "Python logging made (stupidly) simple" -optional = false -python-versions = "<4.0,>=3.5" -groups = ["dev"] -files = [ - {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, - {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} -win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} - -[package.extras] -dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""] - -[[package]] -name = "lsprotocol" -version = "2024.0.0b1" -description = "Python types for Language Server Protocol." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "lsprotocol-2024.0.0b1-py3-none-any.whl", hash = "sha256:93785050ac155ae2be16b1ebfbd74c214feb3d3ef77b10399ce941e5ccef6ebd"}, - {file = "lsprotocol-2024.0.0b1.tar.gz", hash = "sha256:d3667fb70894d361aa6c495c5c8a1b2e6a44be65ff84c21a9cbb67ebfb4830fd"}, -] - -[package.dependencies] -attrs = ">=21.3.0" -cattrs = "!=23.2.1" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["dev"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy" -version = "1.15.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "ordered-set" -version = "4.1.0" -description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, - {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, -] - -[package.extras] -dev = ["black", "mypy", "pytest"] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pycodestyle" -version = "2.13.0" -description = "Python style guide checker" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, - {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, -] - -[[package]] -name = "pydantic" -version = "2.10.6" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, - {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" -typing-extensions = ">=4.12.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.27.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pyflakes" -version = "3.3.2" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, - {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, -] - -[[package]] -name = "pygls" -version = "2.0.0a2" -description = "A pythonic generic language server (pronounced like 'pie glass')" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pygls-2.0.0a2-py3-none-any.whl", hash = "sha256:b202369321409343aa6440d73111d9fa0c22e580466ff1c7696b8358bb91f243"}, - {file = "pygls-2.0.0a2.tar.gz", hash = "sha256:03e00634ed8d989918268aaa4b4a0c3ab857ea2d4ee94514a52efa5ddd6d5d9f"}, -] - -[package.dependencies] -cattrs = ">=23.1.2" -lsprotocol = "2024.0.0b1" - -[package.extras] -ws = ["websockets (>=13.0)"] - -[[package]] -name = "tomlkit" -version = "0.11.8" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, - {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, -] - -[[package]] -name = "types-flake8" -version = "7.2.0.20250330" -description = "Typing stubs for flake8" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "types_flake8-7.2.0.20250330-py3-none-any.whl", hash = "sha256:af31590a269586309b80a439c94e59359e139e17475a8fc8d3c426ab01a40547"}, - {file = "types_flake8-7.2.0.20250330.tar.gz", hash = "sha256:481e5c914a26fabd23e85704ad055f2716f9238740bfe6fe77259c2533cb970c"}, -] - -[package.dependencies] -types-pyflakes = "*" - -[[package]] -name = "types-pyflakes" -version = "3.3.2.20250511" -description = "Typing stubs for pyflakes" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "types_pyflakes-3.3.2.20250511-py3-none-any.whl", hash = "sha256:85802fdd0b64d3553ef12ac0ba02d85c4bbd38747579c544e6bb005ec455becf"}, - {file = "types_pyflakes-3.3.2.20250511.tar.gz", hash = "sha256:d0ef58f9ec15eab2a9e427814f48587be4eb2752a8ae7dec201d65086f50ace2"}, -] - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "watchdog" -version = "4.0.2" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"}, - {file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"}, - {file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"}, - {file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"}, - {file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -description = "A small Python utility to set file creation time on Windows" -optional = false -python-versions = ">=3.5" -groups = ["dev"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, - {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, -] - -[package.extras] -dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "ac458ef2e905b22924e27f83a2877219858789d6310947e73636a6a13007603a" diff --git a/presets/fine_python_lint/poetry.lock b/presets/fine_python_lint/poetry.lock deleted file mode 100644 index 8470d13..0000000 --- a/presets/fine_python_lint/poetry.lock +++ /dev/null @@ -1,867 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "attrs" -version = "25.3.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, -] - -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] - -[[package]] -name = "black" -version = "25.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "cattrs" -version = "24.1.3" -description = "Composable complex class support for attrs and dataclasses." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5"}, - {file = "cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff"}, -] - -[package.dependencies] -attrs = ">=23.1.0" - -[package.extras] -bson = ["pymongo (>=4.4.0)"] -cbor2 = ["cbor2 (>=5.4.6)"] -msgpack = ["msgpack (>=1.0.5)"] -msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] -orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] -pyyaml = ["pyyaml (>=6.0)"] -tomlkit = ["tomlkit (>=0.11.8)"] -ujson = ["ujson (>=5.7.0)"] - -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["dev"] -markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "fine-python-aksem" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_module_exports = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_module_exports"} - -[package.source] -type = "git" -url = "https://github.com/Aksem/fine_python_aksem.git" -reference = "HEAD" -resolved_reference = "56ce22c3fb1228a6e85d1a8727a0fa74787608d9" - -[[package]] -name = "fine-python-ast" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main", "dev"] -files = [ - {file = "fine_python_ast-0.1.0-py3-none-any.whl", hash = "sha256:3dcdcdc40ed89e0b90686f2ad5d358d8edba622a5de4c1f52f2f0787dcc4e07d"}, - {file = "fine_python_ast-0.1.0.tar.gz", hash = "sha256:ebc68aef7d0379f8771f610e436f6075b7c299a8895f1b9b7769fd038f95397e"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" - -[[package]] -name = "fine-python-black" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["dev"] -files = [ - {file = "fine_python_black-0.1.0-py3-none-any.whl", hash = "sha256:27a012123783e4217222546e37280660e5a4948cdd3aa12d7fae7a3ce21a875d"}, - {file = "fine_python_black-0.1.0.tar.gz", hash = "sha256:ce300e897b4d819abe4754177cd5b81d29b2d4d04233942135edc1a8ed234bf7"}, -] - -[package.dependencies] -black = ">=25.1.0,<26.0.0" -finecode_extension_api = "0.1.0" - -[[package]] -name = "fine-python-flake8" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_flake8-0.1.0-py3-none-any.whl", hash = "sha256:7ed7641505936f334396631f779953a13ae18c8f207a8258d722e50fa452d792"}, - {file = "fine_python_flake8-0.1.0.tar.gz", hash = "sha256:ccba9a1ec41f5f3aa756efa63bd64650237ad63d3098aedf673e9c254e502914"}, -] - -[package.dependencies] -fine_python_ast = "0.1.0" -finecode_extension_api = "0.1.0" -flake8 = ">=7.1.2,<8.0.0" -types-flake8 = ">=7.1.0.20241020,<8.0.0.0" - -[[package]] -name = "fine-python-format" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_black = "0.1.0" -fine_python_isort = "0.1.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "959289157b4a3fe7bc21387a3c079c16e9c2a65e" -subdirectory = "presets/fine_python_format" - -[[package]] -name = "fine-python-isort" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["dev"] -files = [ - {file = "fine_python_isort-0.1.0-py3-none-any.whl", hash = "sha256:cbd6cd5502d65122e9f6461758f78db8d9e5628ab97c41a67ee6ef85a3526c8e"}, - {file = "fine_python_isort-0.1.0.tar.gz", hash = "sha256:64468a96b49663226b422885b25de7c22c24f6dafef6c25c1d02ea6e49662b53"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" -isort = ">=5.13,<6" - -[[package]] -name = "fine-python-module-exports" -version = "0.1.0" -description = "" -optional = false -python-versions = ">= 3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_ast = "0.1.0" -finecode_extension_api = "0.1.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "959289157b4a3fe7bc21387a3c079c16e9c2a65e" -subdirectory = "extensions/fine_python_module_exports" - -[[package]] -name = "fine-python-mypy" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_mypy-0.1.0-py3-none-any.whl", hash = "sha256:cf1df00ad835fb85aa26480210a08ff963ffa560db123a62adea4a10a3684563"}, - {file = "fine_python_mypy-0.1.0.tar.gz", hash = "sha256:ece18604bdff098c3fd154cd138af74d82276fec20a1ae7ad03114427688e6f6"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" -mypy = ">=1.15,<2.0" - -[[package]] -name = "fine-python-recommended" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_format = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_format"} -fine_python_lint = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_lint"} - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "959289157b4a3fe7bc21387a3c079c16e9c2a65e" -subdirectory = "presets/fine_python_recommended" - -[[package]] -name = "finecode" -version = "0.2.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["dev"] -files = [ - {file = "finecode-0.2.0-py3-none-any.whl", hash = "sha256:42e4980a219a91e9c8a84e51d5df3f7c43d160230bb0fb6001a3e4f28a42041c"}, - {file = "finecode-0.2.0.tar.gz", hash = "sha256:149001825d27403e18c935f67b710184cea3cf3b846283996a595fabcc41d67e"}, -] - -[package.dependencies] -click = "==8.1.*" -finecode_extension_api = "0.1.0" -loguru = "==0.7.*" -ordered-set = "==4.1.*" -platformdirs = "==4.3.*" -pydantic = "==2.10.*" -pygls = "2.0.0-a2" -tomlkit = "==0.11.*" -watchdog = "==4.0.*" - -[[package]] -name = "finecode-dev-common-preset" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = true - -[package.dependencies] -fine_python_aksem = {git = "https://github.com/Aksem/fine_python_aksem.git"} -fine_python_recommended = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_recommended"} - -[package.source] -type = "directory" -url = "../../finecode_dev_common_preset" - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main", "dev"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "flake8" -version = "7.2.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, - {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.13.0,<2.14.0" -pyflakes = ">=3.3.0,<3.4.0" - -[[package]] -name = "flake8-bugbear" -version = "24.12.12" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" -groups = ["main"] -files = [ - {file = "flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e"}, - {file = "flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -flake8 = ">=6.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -groups = ["dev"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "loguru" -version = "0.7.3" -description = "Python logging made (stupidly) simple" -optional = false -python-versions = "<4.0,>=3.5" -groups = ["dev"] -files = [ - {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, - {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} -win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} - -[package.extras] -dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""] - -[[package]] -name = "lsprotocol" -version = "2024.0.0b1" -description = "Python types for Language Server Protocol." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "lsprotocol-2024.0.0b1-py3-none-any.whl", hash = "sha256:93785050ac155ae2be16b1ebfbd74c214feb3d3ef77b10399ce941e5ccef6ebd"}, - {file = "lsprotocol-2024.0.0b1.tar.gz", hash = "sha256:d3667fb70894d361aa6c495c5c8a1b2e6a44be65ff84c21a9cbb67ebfb4830fd"}, -] - -[package.dependencies] -attrs = ">=21.3.0" -cattrs = "!=23.2.1" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy" -version = "1.15.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "ordered-set" -version = "4.1.0" -description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, - {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, -] - -[package.extras] -dev = ["black", "mypy", "pytest"] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pycodestyle" -version = "2.13.0" -description = "Python style guide checker" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, - {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, -] - -[[package]] -name = "pydantic" -version = "2.10.6" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, - {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" -typing-extensions = ">=4.12.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.27.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pyflakes" -version = "3.3.2" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, - {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, -] - -[[package]] -name = "pygls" -version = "2.0.0a2" -description = "A pythonic generic language server (pronounced like 'pie glass')" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pygls-2.0.0a2-py3-none-any.whl", hash = "sha256:b202369321409343aa6440d73111d9fa0c22e580466ff1c7696b8358bb91f243"}, - {file = "pygls-2.0.0a2.tar.gz", hash = "sha256:03e00634ed8d989918268aaa4b4a0c3ab857ea2d4ee94514a52efa5ddd6d5d9f"}, -] - -[package.dependencies] -cattrs = ">=23.1.2" -lsprotocol = "2024.0.0b1" - -[package.extras] -ws = ["websockets (>=13.0)"] - -[[package]] -name = "tomlkit" -version = "0.11.8" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, - {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, -] - -[[package]] -name = "types-flake8" -version = "7.2.0.20250330" -description = "Typing stubs for flake8" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "types_flake8-7.2.0.20250330-py3-none-any.whl", hash = "sha256:af31590a269586309b80a439c94e59359e139e17475a8fc8d3c426ab01a40547"}, - {file = "types_flake8-7.2.0.20250330.tar.gz", hash = "sha256:481e5c914a26fabd23e85704ad055f2716f9238740bfe6fe77259c2533cb970c"}, -] - -[package.dependencies] -types-pyflakes = "*" - -[[package]] -name = "types-pyflakes" -version = "3.3.2.20250511" -description = "Typing stubs for pyflakes" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "types_pyflakes-3.3.2.20250511-py3-none-any.whl", hash = "sha256:85802fdd0b64d3553ef12ac0ba02d85c4bbd38747579c544e6bb005ec455becf"}, - {file = "types_pyflakes-3.3.2.20250511.tar.gz", hash = "sha256:d0ef58f9ec15eab2a9e427814f48587be4eb2752a8ae7dec201d65086f50ace2"}, -] - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "watchdog" -version = "4.0.2" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"}, - {file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"}, - {file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"}, - {file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"}, - {file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -description = "A small Python utility to set file creation time on Windows" -optional = false -python-versions = ">=3.5" -groups = ["dev"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, - {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, -] - -[package.extras] -dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "503c582ffe07c09630f15e21394a4d290cf2485eb7604eb52716b1bc681132c8" diff --git a/presets/fine_python_recommended/poetry.lock b/presets/fine_python_recommended/poetry.lock deleted file mode 100644 index ffcd3a4..0000000 --- a/presets/fine_python_recommended/poetry.lock +++ /dev/null @@ -1,858 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "attrs" -version = "25.3.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, -] - -[package.extras] -benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] - -[[package]] -name = "black" -version = "25.1.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, - {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, - {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, - {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, - {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, - {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, - {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, - {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, - {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, - {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, - {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, - {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, - {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, - {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, - {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, - {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, - {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, - {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, - {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, - {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, - {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, - {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.10)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "cattrs" -version = "24.1.3" -description = "Composable complex class support for attrs and dataclasses." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "cattrs-24.1.3-py3-none-any.whl", hash = "sha256:adf957dddd26840f27ffbd060a6c4dd3b2192c5b7c2c0525ef1bd8131d8a83f5"}, - {file = "cattrs-24.1.3.tar.gz", hash = "sha256:981a6ef05875b5bb0c7fb68885546186d306f10f0f6718fe9b96c226e68821ff"}, -] - -[package.dependencies] -attrs = ">=23.1.0" - -[package.extras] -bson = ["pymongo (>=4.4.0)"] -cbor2 = ["cbor2 (>=5.4.6)"] -msgpack = ["msgpack (>=1.0.5)"] -msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] -orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] -pyyaml = ["pyyaml (>=6.0)"] -tomlkit = ["tomlkit (>=0.11.8)"] -ujson = ["ujson (>=5.7.0)"] - -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev"] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main", "dev"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] -markers = {main = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""} - -[[package]] -name = "fine-python-aksem" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_module_exports = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "extensions/fine_python_module_exports"} - -[package.source] -type = "git" -url = "https://github.com/Aksem/fine_python_aksem.git" -reference = "HEAD" -resolved_reference = "56ce22c3fb1228a6e85d1a8727a0fa74787608d9" - -[[package]] -name = "fine-python-ast" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main", "dev"] -files = [ - {file = "fine_python_ast-0.1.0-py3-none-any.whl", hash = "sha256:3dcdcdc40ed89e0b90686f2ad5d358d8edba622a5de4c1f52f2f0787dcc4e07d"}, - {file = "fine_python_ast-0.1.0.tar.gz", hash = "sha256:ebc68aef7d0379f8771f610e436f6075b7c299a8895f1b9b7769fd038f95397e"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" - -[[package]] -name = "fine-python-black" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_black-0.1.0-py3-none-any.whl", hash = "sha256:27a012123783e4217222546e37280660e5a4948cdd3aa12d7fae7a3ce21a875d"}, - {file = "fine_python_black-0.1.0.tar.gz", hash = "sha256:ce300e897b4d819abe4754177cd5b81d29b2d4d04233942135edc1a8ed234bf7"}, -] - -[package.dependencies] -black = ">=25.1.0,<26.0.0" -finecode_extension_api = "0.1.0" - -[[package]] -name = "fine-python-flake8" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_flake8-0.1.0-py3-none-any.whl", hash = "sha256:7ed7641505936f334396631f779953a13ae18c8f207a8258d722e50fa452d792"}, - {file = "fine_python_flake8-0.1.0.tar.gz", hash = "sha256:ccba9a1ec41f5f3aa756efa63bd64650237ad63d3098aedf673e9c254e502914"}, -] - -[package.dependencies] -fine_python_ast = "0.1.0" -finecode_extension_api = "0.1.0" -flake8 = ">=7.1.2,<8.0.0" -types-flake8 = ">=7.1.0.20241020,<8.0.0.0" - -[[package]] -name = "fine-python-format" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_format-0.1.0-py3-none-any.whl", hash = "sha256:26315bc8ae5a4830efd9ee41ab3e220beead52c0702cc6f82df720e59dbfbfc2"}, - {file = "fine_python_format-0.1.0.tar.gz", hash = "sha256:8d8031316abed4761d096c2a0c100c8d7cc6d391ea386149ef625964ab80d0d2"}, -] - -[package.dependencies] -fine_python_black = "0.1.0" -fine_python_isort = "0.1.0" - -[[package]] -name = "fine-python-isort" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_isort-0.1.0-py3-none-any.whl", hash = "sha256:cbd6cd5502d65122e9f6461758f78db8d9e5628ab97c41a67ee6ef85a3526c8e"}, - {file = "fine_python_isort-0.1.0.tar.gz", hash = "sha256:64468a96b49663226b422885b25de7c22c24f6dafef6c25c1d02ea6e49662b53"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" -isort = ">=5.13,<6" - -[[package]] -name = "fine-python-lint" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_lint-0.1.0-py3-none-any.whl", hash = "sha256:180517a54a38ab4942bf51611d03a9afd3668e8d11f59e289444c2e1756434de"}, - {file = "fine_python_lint-0.1.0.tar.gz", hash = "sha256:597660e54fe4fa4024b54f0050cdc378c48d0a03fd2938d1c0ae60204a8160d8"}, -] - -[package.dependencies] -fine_python_flake8 = "0.1.0" -fine_python_mypy = "0.1.0" -flake8-bugbear = ">=24.12.12,<25.0.0" - -[[package]] -name = "fine-python-module-exports" -version = "0.1.0" -description = "" -optional = false -python-versions = ">= 3.11, < 3.14" -groups = ["dev"] -files = [] -develop = false - -[package.dependencies] -fine_python_ast = "0.1.0" -finecode_extension_api = "0.1.0" - -[package.source] -type = "git" -url = "https://github.com/finecode-dev/finecode.git" -reference = "HEAD" -resolved_reference = "191730dcfdb08f610064163dd680c9a501c0f5ba" -subdirectory = "extensions/fine_python_module_exports" - -[[package]] -name = "fine-python-mypy" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main"] -files = [ - {file = "fine_python_mypy-0.1.0-py3-none-any.whl", hash = "sha256:cf1df00ad835fb85aa26480210a08ff963ffa560db123a62adea4a10a3684563"}, - {file = "fine_python_mypy-0.1.0.tar.gz", hash = "sha256:ece18604bdff098c3fd154cd138af74d82276fec20a1ae7ad03114427688e6f6"}, -] - -[package.dependencies] -finecode_extension_api = "0.1.0" -mypy = ">=1.15,<2.0" - -[[package]] -name = "finecode" -version = "0.2.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["dev"] -files = [ - {file = "finecode-0.2.0-py3-none-any.whl", hash = "sha256:42e4980a219a91e9c8a84e51d5df3f7c43d160230bb0fb6001a3e4f28a42041c"}, - {file = "finecode-0.2.0.tar.gz", hash = "sha256:149001825d27403e18c935f67b710184cea3cf3b846283996a595fabcc41d67e"}, -] - -[package.dependencies] -click = "==8.1.*" -finecode_extension_api = "0.1.0" -loguru = "==0.7.*" -ordered-set = "==4.1.*" -platformdirs = "==4.3.*" -pydantic = "==2.10.*" -pygls = "2.0.0-a2" -tomlkit = "==0.11.*" -watchdog = "==4.0.*" - -[[package]] -name = "finecode-dev-common-preset" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.11, < 3.14" -groups = ["dev"] -files = [] -develop = true - -[package.dependencies] -fine_python_aksem = {git = "https://github.com/Aksem/fine_python_aksem.git"} -fine_python_recommended = {git = "https://github.com/finecode-dev/finecode.git", subdirectory = "presets/fine_python_recommended"} - -[package.source] -type = "directory" -url = "../../finecode_dev_common_preset" - -[[package]] -name = "finecode-extension-api" -version = "0.1.0" -description = "" -optional = false -python-versions = "<3.14,>=3.11" -groups = ["main", "dev"] -files = [ - {file = "finecode_extension_api-0.1.0-py3-none-any.whl", hash = "sha256:55d4448a1a2f7224c3fdf05879fa9782246c0a85dc1dfb17543374fd808c2b5b"}, - {file = "finecode_extension_api-0.1.0.tar.gz", hash = "sha256:dee16c180e4cd318f71bf7a94b6ae425667153f8fb62630ab60db9b034f45595"}, -] - -[package.dependencies] -pydantic = ">=2.10.6,<3.0.0" -typing-extensions = ">=4.12.2,<5.0.0" - -[[package]] -name = "flake8" -version = "7.2.0" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "flake8-7.2.0-py2.py3-none-any.whl", hash = "sha256:93b92ba5bdb60754a6da14fa3b93a9361fd00a59632ada61fd7b130436c40343"}, - {file = "flake8-7.2.0.tar.gz", hash = "sha256:fa558ae3f6f7dbf2b4f22663e5343b6b6023620461f8d4ff2019ef4b5ee70426"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.13.0,<2.14.0" -pyflakes = ">=3.3.0,<3.4.0" - -[[package]] -name = "flake8-bugbear" -version = "24.12.12" -description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -optional = false -python-versions = ">=3.8.1" -groups = ["main"] -files = [ - {file = "flake8_bugbear-24.12.12-py3-none-any.whl", hash = "sha256:1b6967436f65ca22a42e5373aaa6f2d87966ade9aa38d4baf2a1be550767545e"}, - {file = "flake8_bugbear-24.12.12.tar.gz", hash = "sha256:46273cef0a6b6ff48ca2d69e472f41420a42a46e24b2a8972e4f0d6733d12a64"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -flake8 = ">=6.0.0" - -[package.extras] -dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -groups = ["main"] -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "loguru" -version = "0.7.3" -description = "Python logging made (stupidly) simple" -optional = false -python-versions = "<4.0,>=3.5" -groups = ["dev"] -files = [ - {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, - {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} -win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} - -[package.extras] -dev = ["Sphinx (==8.1.3) ; python_version >= \"3.11\"", "build (==1.2.2) ; python_version >= \"3.11\"", "colorama (==0.4.5) ; python_version < \"3.8\"", "colorama (==0.4.6) ; python_version >= \"3.8\"", "exceptiongroup (==1.1.3) ; python_version >= \"3.7\" and python_version < \"3.11\"", "freezegun (==1.1.0) ; python_version < \"3.8\"", "freezegun (==1.5.0) ; python_version >= \"3.8\"", "mypy (==v0.910) ; python_version < \"3.6\"", "mypy (==v0.971) ; python_version == \"3.6\"", "mypy (==v1.13.0) ; python_version >= \"3.8\"", "mypy (==v1.4.1) ; python_version == \"3.7\"", "myst-parser (==4.0.0) ; python_version >= \"3.11\"", "pre-commit (==4.0.1) ; python_version >= \"3.9\"", "pytest (==6.1.2) ; python_version < \"3.8\"", "pytest (==8.3.2) ; python_version >= \"3.8\"", "pytest-cov (==2.12.1) ; python_version < \"3.8\"", "pytest-cov (==5.0.0) ; python_version == \"3.8\"", "pytest-cov (==6.0.0) ; python_version >= \"3.9\"", "pytest-mypy-plugins (==1.9.3) ; python_version >= \"3.6\" and python_version < \"3.8\"", "pytest-mypy-plugins (==3.1.0) ; python_version >= \"3.8\"", "sphinx-rtd-theme (==3.0.2) ; python_version >= \"3.11\"", "tox (==3.27.1) ; python_version < \"3.8\"", "tox (==4.23.2) ; python_version >= \"3.8\"", "twine (==6.0.1) ; python_version >= \"3.11\""] - -[[package]] -name = "lsprotocol" -version = "2024.0.0b1" -description = "Python types for Language Server Protocol." -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "lsprotocol-2024.0.0b1-py3-none-any.whl", hash = "sha256:93785050ac155ae2be16b1ebfbd74c214feb3d3ef77b10399ce941e5ccef6ebd"}, - {file = "lsprotocol-2024.0.0b1.tar.gz", hash = "sha256:d3667fb70894d361aa6c495c5c8a1b2e6a44be65ff84c21a9cbb67ebfb4830fd"}, -] - -[package.dependencies] -attrs = ">=21.3.0" -cattrs = "!=23.2.1" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mypy" -version = "1.15.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, - {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b"}, - {file = "mypy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3"}, - {file = "mypy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b"}, - {file = "mypy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f"}, - {file = "mypy-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e"}, - {file = "mypy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c"}, - {file = "mypy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f"}, - {file = "mypy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd"}, - {file = "mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464"}, - {file = "mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee"}, - {file = "mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e"}, - {file = "mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445"}, - {file = "mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5"}, - {file = "mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036"}, - {file = "mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357"}, - {file = "mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078"}, - {file = "mypy-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5"}, - {file = "mypy-1.15.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b"}, - {file = "mypy-1.15.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2"}, - {file = "mypy-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980"}, - {file = "mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e"}, - {file = "mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43"}, -] - -[package.dependencies] -mypy_extensions = ">=1.0.0" -typing_extensions = ">=4.6.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, - {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, -] - -[[package]] -name = "ordered-set" -version = "4.1.0" -description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, - {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, -] - -[package.extras] -dev = ["black", "mypy", "pytest"] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.8" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.9" -groups = ["main", "dev"] -files = [ - {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, - {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.14.1)"] - -[[package]] -name = "pycodestyle" -version = "2.13.0" -description = "Python style guide checker" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pycodestyle-2.13.0-py2.py3-none-any.whl", hash = "sha256:35863c5974a271c7a726ed228a14a4f6daf49df369d8c50cd9a6f58a5e143ba9"}, - {file = "pycodestyle-2.13.0.tar.gz", hash = "sha256:c8415bf09abe81d9c7f872502a6eee881fbe85d8763dd5b9924bb0a01d67efae"}, -] - -[[package]] -name = "pydantic" -version = "2.10.6" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, - {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" -typing-extensions = ">=4.12.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] - -[[package]] -name = "pydantic-core" -version = "2.27.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pyflakes" -version = "3.3.2" -description = "passive checker of Python programs" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pyflakes-3.3.2-py2.py3-none-any.whl", hash = "sha256:5039c8339cbb1944045f4ee5466908906180f13cc99cc9949348d10f82a5c32a"}, - {file = "pyflakes-3.3.2.tar.gz", hash = "sha256:6dfd61d87b97fba5dcfaaf781171ac16be16453be6d816147989e7f6e6a9576b"}, -] - -[[package]] -name = "pygls" -version = "2.0.0a2" -description = "A pythonic generic language server (pronounced like 'pie glass')" -optional = false -python-versions = ">=3.9" -groups = ["dev"] -files = [ - {file = "pygls-2.0.0a2-py3-none-any.whl", hash = "sha256:b202369321409343aa6440d73111d9fa0c22e580466ff1c7696b8358bb91f243"}, - {file = "pygls-2.0.0a2.tar.gz", hash = "sha256:03e00634ed8d989918268aaa4b4a0c3ab857ea2d4ee94514a52efa5ddd6d5d9f"}, -] - -[package.dependencies] -cattrs = ">=23.1.2" -lsprotocol = "2024.0.0b1" - -[package.extras] -ws = ["websockets (>=13.0)"] - -[[package]] -name = "tomlkit" -version = "0.11.8" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "tomlkit-0.11.8-py3-none-any.whl", hash = "sha256:8c726c4c202bdb148667835f68d68780b9a003a9ec34167b6c673b38eff2a171"}, - {file = "tomlkit-0.11.8.tar.gz", hash = "sha256:9330fc7faa1db67b541b28e62018c17d20be733177d290a13b24c62d1614e0c3"}, -] - -[[package]] -name = "types-flake8" -version = "7.2.0.20250330" -description = "Typing stubs for flake8" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "types_flake8-7.2.0.20250330-py3-none-any.whl", hash = "sha256:af31590a269586309b80a439c94e59359e139e17475a8fc8d3c426ab01a40547"}, - {file = "types_flake8-7.2.0.20250330.tar.gz", hash = "sha256:481e5c914a26fabd23e85704ad055f2716f9238740bfe6fe77259c2533cb970c"}, -] - -[package.dependencies] -types-pyflakes = "*" - -[[package]] -name = "types-pyflakes" -version = "3.3.2.20250511" -description = "Typing stubs for pyflakes" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "types_pyflakes-3.3.2.20250511-py3-none-any.whl", hash = "sha256:85802fdd0b64d3553ef12ac0ba02d85c4bbd38747579c544e6bb005ec455becf"}, - {file = "types_pyflakes-3.3.2.20250511.tar.gz", hash = "sha256:d0ef58f9ec15eab2a9e427814f48587be4eb2752a8ae7dec201d65086f50ace2"}, -] - -[[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main", "dev"] -files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, -] - -[[package]] -name = "watchdog" -version = "4.0.2" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -files = [ - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"}, - {file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"}, - {file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"}, - {file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"}, - {file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"}, -] - -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - -[[package]] -name = "win32-setctime" -version = "1.2.0" -description = "A small Python utility to set file creation time on Windows" -optional = false -python-versions = ">=3.5" -groups = ["dev"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, - {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, -] - -[package.extras] -dev = ["black (>=19.3b0) ; python_version >= \"3.6\"", "pytest (>=4.6.2)"] - -[metadata] -lock-version = "2.1" -python-versions = ">=3.11, < 3.14" -content-hash = "da67317bd7ee70f58ec5be079ab16636dfdc7086e21d68af48d4d5bc37c1cebc" From 45107365a6b594994ae12aaf4b49be9d15714f1d Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 07:23:35 +0200 Subject: [PATCH 20/46] finecode_extension_api v0.2.0 --- finecode_extension_api/pyproject.toml | 4 ++-- .../src/finecode_extension_api/actions/build.py | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/finecode_extension_api/pyproject.toml b/finecode_extension_api/pyproject.toml index 8484621..82081c6 100644 --- a/finecode_extension_api/pyproject.toml +++ b/finecode_extension_api/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "finecode-extension-api" -version = "0.1.3" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" @@ -8,7 +8,7 @@ requires-python = ">=3.11, < 3.14" dependencies = ["typing-extensions (>=4.12.2,<5.0.0)"] [dependency-groups] -dev_workspace = ["finecode==0.2.6.dev14+g790c37afd.d20250821"] +dev_workspace = ["finecode==0.3.0"] dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] [tool.finecode] diff --git a/finecode_extension_api/src/finecode_extension_api/actions/build.py b/finecode_extension_api/src/finecode_extension_api/actions/build.py index 7f0d219..1ae2fad 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/build.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/build.py @@ -17,7 +17,7 @@ class BuildRunPayload(code_action.RunActionPayload): # packages can have different layouts. Use service (TODO) to get package source # directory path and avoid need to handle all possible cases by yourself. package_root_path: pathlib.Path - build_type: typing.Literal['release'] | typing.Literal['debug'] = 'release' + build_type: typing.Literal["release"] | typing.Literal["debug"] = "release" # TODO: entrypoint # TODO: package type # TODO: target platform? (including version etc) @@ -44,7 +44,7 @@ def update(self, other: code_action.RunActionResult) -> None: return def to_text(self) -> str | textstyler.StyledText: - return '' + return "" # general build action: any type of project should be built: library(pure and not pure python), application(both pure distributed as python package and application transformed to executable) @@ -56,9 +56,7 @@ def to_text(self) -> str | textstyler.StyledText: # 5. Application packaged to executable with pyinstaller or similar tool. # # Customization examples: -# - Recognize constructs(syntax, imports) supported only by higher versions of python and replace them by alternatives from older python. One universal wheel will become version-specific wheel. +# - Recognize constructs(syntax, imports) supported only by higher versions of python and replace them by alternatives from older python. One universal wheel will become version-specific wheel. # - the same could be applied for platform-specific functionalities # - optimize implementation -type BuildAction = code_action.Action[ - BuildRunPayload, BuildRunContext, BuildRunResult -] +type BuildAction = code_action.Action[BuildRunPayload, BuildRunContext, BuildRunResult] From ffe04738c394db11ae5122df2eaed796fe9848ab Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 08:22:42 +0200 Subject: [PATCH 21/46] Finish error handling in read config handlers --- .../action_handlers/prepare_envs_read_configs.py | 4 +--- .../action_handlers/prepare_runners_read_configs.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py index 982d8ba..d30ac14 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py @@ -1,8 +1,6 @@ import dataclasses import shutil -import tomlkit - from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_envs as prepare_envs_action from finecode_extension_api.interfaces import ( @@ -41,7 +39,7 @@ async def run( [env_info.project_def_path for env_info in payload.envs] ) if len(project_defs_pathes) != 1: - ... # TODO: error + raise code_action.ActionFailedException("PrepareEnvsReadConfigsHandler supports only reading config of envs from the current project") project_raw_config = await self.project_info_provider.get_project_raw_config() diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py index db71a67..3fc5124 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py @@ -40,7 +40,7 @@ async def run( [env_info.project_def_path for env_info in payload.envs] ) if len(project_defs_pathes) != 1: - ... # TODO: error + raise code_action.ActionFailedException("PrepareRunnersReadConfigsHandler supports only reading config of envs from the current project") project_raw_config = await self.project_info_provider.get_project_raw_config() From d7991763120bd0329c590e65cf94dcc34847cb70 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 08:25:11 +0200 Subject: [PATCH 22/46] Add missing dataclass decorator in text document code action --- .../actions/ide/text_document_code_action.py | 1 + 1 file changed, 1 insertion(+) diff --git a/finecode_extension_api/src/finecode_extension_api/actions/ide/text_document_code_action.py b/finecode_extension_api/src/finecode_extension_api/actions/ide/text_document_code_action.py index f57b52c..16e9bfd 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/ide/text_document_code_action.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/ide/text_document_code_action.py @@ -33,6 +33,7 @@ class CodeActionTriggerKind(enum.IntEnum): class Diagnostic: ... +@dataclasses.dataclass class CodeActionContext: diagnostics: list[Diagnostic] only: CodeActionKind | None From f8aa26fbb8fb7366c4897637726adb1807b88e7a Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 08:25:33 +0200 Subject: [PATCH 23/46] Clean ER readme --- finecode_extension_runner/README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/finecode_extension_runner/README.md b/finecode_extension_runner/README.md index 0eefc7a..b92e91e 100644 --- a/finecode_extension_runner/README.md +++ b/finecode_extension_runner/README.md @@ -1,5 +1 @@ -# finecode_extension_runner - -Extension runner component for FineCode - handles execution environment for running actions in isolated environments. - -This package contains the extension runner that was previously part of the main finecode package (`finecode.extension_runner`). \ No newline at end of file +# FineCode Python Extension Runner From d250566fc3d5238bcbc83a4767f517fcd63e6325 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 14:44:59 +0200 Subject: [PATCH 24/46] Map exceptions in iactionrunner implementation --- .../interfaces/iactionrunner.py | 3 +-- .../finecode_extension_runner/di/bootstrap.py | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py b/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py index da6ef5b..aa78d09 100644 --- a/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py +++ b/finecode_extension_api/src/finecode_extension_api/interfaces/iactionrunner.py @@ -19,5 +19,4 @@ class ActionNotFound(BaseRunActionException): ... class InvalidActionRunPayload(BaseRunActionException): ... -class ActionRunFailed(BaseRunActionException): - pass +class ActionRunFailed(BaseRunActionException): ... diff --git a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py index 64493d8..3cd5a07 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py +++ b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py @@ -29,8 +29,7 @@ loguru_logger, project_info_provider, ) - -from ._state import container, factories +from finecode_extension_runner.di import _state def bootstrap(get_document_func: Callable, save_document_func: Callable): @@ -49,21 +48,21 @@ def bootstrap(get_document_func: Callable, save_document_func: Callable): action_runner_instance = action_runner.ActionRunner( internal_service_func=run_action_wrapper ) - container[ilogger.ILogger] = logger_instance - container[icommandrunner.ICommandRunner] = command_runner_instance - container[ifilemanager.IFileManager] = file_manager_instance - container[icache.ICache] = cache_instance - container[iactionrunner.IActionRunner] = action_runner_instance + _state.container[ilogger.ILogger] = logger_instance + _state.container[icommandrunner.ICommandRunner] = command_runner_instance + _state.container[ifilemanager.IFileManager] = file_manager_instance + _state.container[icache.ICache] = cache_instance + _state.container[iactionrunner.IActionRunner] = action_runner_instance if fine_python_ast is not None: - factories[fine_python_ast.IPythonSingleAstProvider] = ( + _state.factories[fine_python_ast.IPythonSingleAstProvider] = ( python_single_ast_provider_factory ) if fine_python_mypy is not None: - factories[fine_python_mypy.IMypySingleAstProvider] = ( + _state.factories[fine_python_mypy.IMypySingleAstProvider] = ( mypy_single_ast_provider_factory ) - factories[iprojectinfoprovider.IProjectInfoProvider] = project_info_provider_factory + _state.factories[iprojectinfoprovider.IProjectInfoProvider] = project_info_provider_factory # TODO: parameters from config @@ -73,8 +72,11 @@ async def run_action_wrapper( ) -> dict[str, Any]: request = schemas.RunActionRequest(action_name=action_name, params=payload) options = schemas.RunActionOptions(result_format="json") - # TODO: map exceptions to iactionrunner - response = await run_action.run_action(request=request, options=options) + + try: + response = await run_action.run_action(request=request, options=options) + except run_action.ActionFailedException as exception: + raise iactionrunner.ActionRunFailed(exception.message) return response.result From 1d5d716156c7630470fb74b9526aeeb9c1fb23db Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 15:02:41 +0200 Subject: [PATCH 25/46] Fix dump config CLI command and add textual representation of dumped config --- .../finecode_extension_api/actions/dump_config.py | 4 +++- src/finecode/cli_app/dump_config.py | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py b/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py index 6fb8b1c..765edb6 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py @@ -2,6 +2,7 @@ import pathlib import sys import typing +import pprint if sys.version_info >= (3, 12): from typing import override @@ -46,7 +47,8 @@ def update(self, other: code_action.RunActionResult) -> None: self.config_dump = other.config_dump def to_text(self) -> str | textstyler.StyledText: - return "" + formatted_dump_str = pprint.pformat(self.config_dump) + return formatted_dump_str class DumpConfigAction(code_action.Action): diff --git a/src/finecode/cli_app/dump_config.py b/src/finecode/cli_app/dump_config.py index e489eab..a46b1ea 100644 --- a/src/finecode/cli_app/dump_config.py +++ b/src/finecode/cli_app/dump_config.py @@ -4,7 +4,7 @@ from loguru import logger from finecode import context, services -from finecode.config import read_configs +from finecode.config import read_configs, config_models from finecode.runner import manager as runner_manager @@ -27,6 +27,19 @@ async def dump_config(workdir_path: pathlib.Path, project_name: str): for project_dir_path, project in ws_context.ws_projects.items() if project.name == project_name } + + # read configs without presets, this is required to be able to start runners in + # the next step + for project in ws_context.ws_projects.values(): + try: + await read_configs.read_project_config( + project=project, ws_context=ws_context, resolve_presets=False + ) + except config_models.ConfigurationError as exception: + raise DumpFailed( + f"Reading project configs(without presets) in {project.dir_path} failed: {exception.message}" + ) + # start runner to init project config try: From b4f9c10ffbd1f212f2930e1d9f8e8aef1e33afe7 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 15:10:02 +0200 Subject: [PATCH 26/46] Use deepmerge to merge handler configs instead of just updating dict --- .../_services/run_action.py | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py index 144959e..e9c4965 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py +++ b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py @@ -5,6 +5,7 @@ import time import typing +import deepmerge from loguru import logger from pydantic.dataclasses import dataclass as pydantic_dataclass @@ -19,7 +20,17 @@ last_run_id: int = 0 partial_result_sender: partial_result_sender_module.PartialResultSender - +handler_config_merger = deepmerge.Merger( + [ + (list, ["override"]), + (dict, ["merge"]), + (set, ["override"]) + ], + # all other types: + ["override"], + # strategies in the case where the types conflict: + ["override"] +) class ActionFailedException(Exception): def __init__(self, message: str) -> None: @@ -401,10 +412,14 @@ async def execute_action_handler( handler.source, None ) handler_raw_config = {} - # TODO: deep merge instead? if handler_global_config is not None: - handler_raw_config.update(handler_global_config) - handler_raw_config.update(handler.config) + handler_raw_config = handler_global_config + if handler_raw_config == {}: + # still empty, just assign + handler_raw_config = handler.config + else: + # not empty anymore, deep merge + handler_config_merger.merge(handler_raw_config, handler.config) def get_handler_config(param_type): # TODO: validation errors @@ -564,6 +579,7 @@ async def run_subresult_coros_sequentially( try: coro_result = await coro except Exception as e: + logger.error(f"Unhandled exception in subresult coroutine({action_name}, run {run_id}):") logger.exception(e) raise ActionFailedException( f"Running action handlers of '{action_name}' failed(Run {run_id}): {e}" From 9ca7c4f74a6cc393cce263c7c2d44970ee5e122f Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Mon, 25 Aug 2025 15:13:19 +0200 Subject: [PATCH 27/46] Remove experimental mcp_server, it will be in another branch --- src/finecode/mcp_server.py | 43 -------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 src/finecode/mcp_server.py diff --git a/src/finecode/mcp_server.py b/src/finecode/mcp_server.py deleted file mode 100644 index ee0b132..0000000 --- a/src/finecode/mcp_server.py +++ /dev/null @@ -1,43 +0,0 @@ -import asyncio -import pathlib -from mcp.server.fastmcp import FastMCP - -from finecode import context - - -async def create_and_start_mcp_server(port: int, ws_context: context.WorkspaceContext) -> asyncio.Task: - # TODO: use Server instead of FastMCP to be able to call tools dynamically - # example: https://github.com/modelcontextprotocol/servers/blob/main/src/git/src/mcp_server_git/server.py - mcp = FastMCP("FineCode", port=8776, json_response=True) - - def list_actions(): - return { - "actions": [ - { "name": "Lint" }, - { "name": "Format" } - ] - } - - mcp.add_tool(fn=list_actions, name="list_actions", description="List actions available for developer in development workspace", annotations=None) - - def lint(): - print("perform linting...") - - mcp.add_tool(fn=lint, name="lint", description="Lint either the whole workspace or single project or even file", annotations=None) - # TODO: ~~projects as resource?~~ - # TODO: finecode actions as mcp tools? - - mcp.call_tool - # mcp.run() - server_task = asyncio.create_task(mcp.run_streamable_http_async()) - return server_task - - -async def start(): - ws_context = context.WorkspaceContext([pathlib.Path('/home/user/Development/FineCode/finecode')]) - await create_and_start_mcp_server(8776, ws_context) - while True: - await asyncio.sleep(1) - -if __name__ == "__main__": - asyncio.run(start()) From 54d8a5e6dcde23dca912ef563df93d1edc9888ad Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Tue, 26 Aug 2025 07:18:11 +0200 Subject: [PATCH 28/46] Finish error handling in prepare_runners and prepare_envs handlers. Extend iprojectinfoprovider with methods for current project and project raw config of any project, not only current --- .../actions/dump_config.py | 2 +- .../interfaces/iprojectinfoprovider.py | 9 +- .../_services/run_action.py | 13 +- .../dependency_config_utils.py | 101 ++++++++----- .../prepare_envs_read_configs.py | 31 ++-- .../prepare_runners_read_configs.py | 31 ++-- .../finecode_extension_runner/di/bootstrap.py | 30 +++- .../impls/project_info_provider.py | 26 +++- .../finecode_extension_runner/lsp_server.py | 133 +++++++++--------- .../src/finecode_extension_runner/services.py | 26 ++-- src/finecode/cli_app/dump_config.py | 5 +- src/finecode/cli_app/prepare_envs.py | 2 +- src/finecode/config/collect_actions.py | 6 +- src/finecode/runner/manager.py | 12 +- 14 files changed, 263 insertions(+), 164 deletions(-) diff --git a/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py b/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py index 765edb6..4cfa428 100644 --- a/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py +++ b/finecode_extension_api/src/finecode_extension_api/actions/dump_config.py @@ -1,8 +1,8 @@ import dataclasses import pathlib +import pprint import sys import typing -import pprint if sys.version_info >= (3, 12): from typing import override diff --git a/finecode_extension_api/src/finecode_extension_api/interfaces/iprojectinfoprovider.py b/finecode_extension_api/src/finecode_extension_api/interfaces/iprojectinfoprovider.py index 32763b9..6e3af54 100644 --- a/finecode_extension_api/src/finecode_extension_api/interfaces/iprojectinfoprovider.py +++ b/finecode_extension_api/src/finecode_extension_api/interfaces/iprojectinfoprovider.py @@ -1,5 +1,12 @@ +import pathlib from typing import Any, Protocol class IProjectInfoProvider(Protocol): - async def get_project_raw_config(self) -> dict[str, Any]: ... + def get_current_project_def_path(self) -> pathlib.Path: ... + + async def get_project_raw_config( + self, project_def_path: pathlib.Path + ) -> dict[str, Any]: ... + + async def get_current_project_raw_config(self) -> dict[str, Any]: ... diff --git a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py index e9c4965..84f9fbe 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py +++ b/finecode_extension_runner/src/finecode_extension_runner/_services/run_action.py @@ -21,17 +21,14 @@ last_run_id: int = 0 partial_result_sender: partial_result_sender_module.PartialResultSender handler_config_merger = deepmerge.Merger( - [ - (list, ["override"]), - (dict, ["merge"]), - (set, ["override"]) - ], + [(list, ["override"]), (dict, ["merge"]), (set, ["override"])], # all other types: ["override"], # strategies in the case where the types conflict: - ["override"] + ["override"], ) + class ActionFailedException(Exception): def __init__(self, message: str) -> None: self.message = message @@ -579,7 +576,9 @@ async def run_subresult_coros_sequentially( try: coro_result = await coro except Exception as e: - logger.error(f"Unhandled exception in subresult coroutine({action_name}, run {run_id}):") + logger.error( + f"Unhandled exception in subresult coroutine({action_name}, run {run_id}):" + ) logger.exception(e) raise ActionFailedException( f"Running action handlers of '{action_name}' failed(Run {run_id}): {e}" diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py index cd4ed23..1a0e93e 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/dependency_config_utils.py @@ -5,46 +5,67 @@ def make_project_config_pip_compatible( project_raw_config: dict[str, typing.Any], config_file_path: pathlib.Path ) -> None: - # TODO: what to do with included groups in dependency groups? Inherit config from its env? finecode_config = project_raw_config.get("tool", {}).get("finecode", {}) # apply changes to dependencies from env configuration to deps groups - for env_name, env_config in finecode_config.get("env", {}).items(): - if "dependencies" not in env_config: - continue - - env_deps_group = project_raw_config.get("dependency-groups", {}).get( - env_name, [] + for env_name in finecode_config.get("env", {}).keys(): + make_env_deps_pip_compatible( + env_name=env_name, + project_raw_config=project_raw_config, + config_file_path=config_file_path, ) - dependencies = env_config["dependencies"] - for dep_name, dep_params in dependencies.items(): - # handle 'path'. 'editable' cannot be handled here because dependency - # specifier doesn't support it. It will read and processed by - # `install_deps` action - if "path" in dep_params: - # replace dependency version / source in dependency group to this path - # - # check all dependencies because it can be duplicated: e.g. as explicit - # dependency and as dependency of action handler. - dep_indexes_in_group: list[int] = [] - for idx, dep in enumerate(env_deps_group): - # check for string because dependency can be also dictionary like '{ "include-group": "runtime"}' - if isinstance(dep, str) and get_dependency_name(dep) == dep_name: + + +def make_env_deps_pip_compatible( + env_name: str, + project_raw_config: dict[str, typing.Any], + config_file_path: pathlib.Path, +) -> None: + env_config = ( + project_raw_config.get("tool", {}) + .get("finecode", {}) + .get("env", {}) + .get(env_name, None) + ) + if env_config is None or "dependencies" not in env_config: + return + + env_deps_group = project_raw_config.get("dependency-groups", {}).get(env_name, []) + dependencies = env_config["dependencies"] + for dep_name, dep_params in dependencies.items(): + # handle 'path'. 'editable' cannot be handled here because dependency + # specifier doesn't support it. It will read and processed by + # `install_deps` action + if "path" in dep_params: + # replace dependency version / source in dependency group to this path + # + # check all dependencies because it can be duplicated: e.g. as explicit + # dependency and as dependency of action handler. + dep_indexes_in_group: list[int] = [] + for idx, dep in enumerate(env_deps_group): + if isinstance(dep, dict): + if "include-group" in dep: + included_group = dep["include-group"] + make_env_deps_pip_compatible( + env_name=included_group, + project_raw_config=project_raw_config, + config_file_path=config_file_path, + ) + elif isinstance(dep, str): + if get_dependency_name(dep) == dep_name: dep_indexes_in_group.append(idx) - if len(dep_indexes_in_group) == 0: - continue + if len(dep_indexes_in_group) == 0: + continue - resolved_path_to_dep = pathlib.Path(dep_params["path"]) - if not resolved_path_to_dep.is_absolute(): - # resolve relative to project dir where project def file is - resolved_path_to_dep = ( - config_file_path.parent / resolved_path_to_dep - ) - new_dep_str_in_group = ( - f"{dep_name} @ file://{resolved_path_to_dep.as_posix()}" - ) - for idx in dep_indexes_in_group: - env_deps_group[idx] = new_dep_str_in_group + resolved_path_to_dep = pathlib.Path(dep_params["path"]) + if not resolved_path_to_dep.is_absolute(): + # resolve relative to project dir where project def file is + resolved_path_to_dep = config_file_path.parent / resolved_path_to_dep + new_dep_str_in_group = ( + f"{dep_name} @ file://{resolved_path_to_dep.as_posix()}" + ) + for idx in dep_indexes_in_group: + env_deps_group[idx] = new_dep_str_in_group def get_dependency_name(dependency_str: str) -> str: @@ -58,11 +79,6 @@ def get_dependency_name(dependency_str: str) -> str: return dependency_str -class FailedToGetDependencies(Exception): - def __init__(self, message: str) -> None: - self.message = message - - def raw_dep_to_dep_dict(raw_dep: str, env_deps_config: dict) -> dict[str, str | bool]: name = get_dependency_name(raw_dep) version_or_source = raw_dep[len(name) :] @@ -73,3 +89,10 @@ def raw_dep_to_dep_dict(raw_dep: str, env_deps_config: dict) -> dict[str, str | "editable": editable, } return dep_dict + + +__all__ = [ + "make_project_config_pip_compatible", + "get_dependency_name", + "raw_dep_to_dep_dict", +] diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py index d30ac14..49c4cbf 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_envs_read_configs.py @@ -1,5 +1,8 @@ +import asyncio import dataclasses +import pathlib import shutil +import typing from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_envs as prepare_envs_action @@ -38,22 +41,30 @@ async def run( project_defs_pathes = set( [env_info.project_def_path for env_info in payload.envs] ) - if len(project_defs_pathes) != 1: - raise code_action.ActionFailedException("PrepareEnvsReadConfigsHandler supports only reading config of envs from the current project") + raw_config_by_project_def_path: dict[pathlib.Path, dict[str, typing.Any]] = {} - project_raw_config = await self.project_info_provider.get_project_raw_config() + get_config_tasks: list[asyncio.Task] = [] + async with asyncio.TaskGroup() as tg: + for project_def_path in project_defs_pathes: + task = tg.create_task( + self.project_info_provider.get_project_raw_config(project_def_path) + ) + get_config_tasks.append(task) - project_def_path = project_defs_pathes.pop() - project_dir_path = project_def_path.parent - - dependency_config_utils.make_project_config_pip_compatible( - project_raw_config, project_def_path - ) + for idx, project_def_path in enumerate(project_defs_pathes): + project_raw_config = get_config_tasks[idx].result() + dependency_config_utils.make_project_config_pip_compatible( + project_raw_config, project_def_path + ) + raw_config_by_project_def_path[project_def_path] = project_raw_config for env_info in payload.envs: run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = ( - project_def_path + env_info.project_def_path ) + project_raw_config = raw_config_by_project_def_path[ + env_info.project_def_path + ] run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = ( project_raw_config ) diff --git a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py index 3fc5124..e9408b0 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py +++ b/finecode_extension_runner/src/finecode_extension_runner/action_handlers/prepare_runners_read_configs.py @@ -1,5 +1,8 @@ +import asyncio import dataclasses +import pathlib import shutil +import typing from finecode_extension_api import code_action from finecode_extension_api.actions import prepare_runners as prepare_runners_action @@ -39,22 +42,30 @@ async def run( project_defs_pathes = set( [env_info.project_def_path for env_info in payload.envs] ) - if len(project_defs_pathes) != 1: - raise code_action.ActionFailedException("PrepareRunnersReadConfigsHandler supports only reading config of envs from the current project") + raw_config_by_project_def_path: dict[pathlib.Path, dict[str, typing.Any]] = {} - project_raw_config = await self.project_info_provider.get_project_raw_config() + get_config_tasks: list[asyncio.Task] = [] + async with asyncio.TaskGroup() as tg: + for project_def_path in project_defs_pathes: + task = tg.create_task( + self.project_info_provider.get_project_raw_config(project_def_path) + ) + get_config_tasks.append(task) - project_def_path = project_defs_pathes.pop() - project_dir_path = project_def_path.parent - - dependency_config_utils.make_project_config_pip_compatible( - project_raw_config, project_def_path - ) + for idx, project_def_path in enumerate(project_defs_pathes): + project_raw_config = get_config_tasks[idx].result() + dependency_config_utils.make_project_config_pip_compatible( + project_raw_config, project_def_path + ) + raw_config_by_project_def_path[project_def_path] = project_raw_config for env_info in payload.envs: run_context.project_def_path_by_venv_dir_path[env_info.venv_dir_path] = ( - project_def_path + env_info.project_def_path ) + project_raw_config = raw_config_by_project_def_path[ + env_info.project_def_path + ] run_context.project_def_by_venv_dir_path[env_info.venv_dir_path] = ( project_raw_config ) diff --git a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py index 3cd5a07..45d2cfd 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py +++ b/finecode_extension_runner/src/finecode_extension_runner/di/bootstrap.py @@ -1,4 +1,6 @@ -from typing import Any, Callable, Type, TypeVar +import functools +import pathlib +from typing import Any, Awaitable, Callable, Type, TypeVar try: import fine_python_ast @@ -21,6 +23,7 @@ ) from finecode_extension_runner import global_state, schemas from finecode_extension_runner._services import run_action +from finecode_extension_runner.di import _state from finecode_extension_runner.impls import ( action_runner, command_runner, @@ -29,10 +32,14 @@ loguru_logger, project_info_provider, ) -from finecode_extension_runner.di import _state -def bootstrap(get_document_func: Callable, save_document_func: Callable): +def bootstrap( + get_document_func: Callable, + save_document_func: Callable, + project_def_path_getter: Callable[[], pathlib.Path], + project_raw_config_getter: Callable[[str], Awaitable[dict[str, Any]]], +): # logger_instance = loguru_logger.LoguruLogger() logger_instance = loguru_logger.get_logger() command_runner_instance = command_runner.CommandRunner(logger=logger_instance) @@ -62,7 +69,11 @@ def bootstrap(get_document_func: Callable, save_document_func: Callable): _state.factories[fine_python_mypy.IMypySingleAstProvider] = ( mypy_single_ast_provider_factory ) - _state.factories[iprojectinfoprovider.IProjectInfoProvider] = project_info_provider_factory + _state.factories[iprojectinfoprovider.IProjectInfoProvider] = functools.partial( + project_info_provider_factory, + project_def_path_getter=project_def_path_getter, + project_raw_config_getter=project_raw_config_getter, + ) # TODO: parameters from config @@ -97,5 +108,12 @@ def mypy_single_ast_provider_factory(container): ) -def project_info_provider_factory(container): - return project_info_provider.ProjectInfoProvider() +def project_info_provider_factory( + container, + project_def_path_getter: Callable[[], pathlib.Path], + project_raw_config_getter: Callable[[str], Awaitable[dict[str, Any]]], +): + return project_info_provider.ProjectInfoProvider( + project_def_path_getter=project_def_path_getter, + project_raw_config_getter=project_raw_config_getter, + ) diff --git a/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py b/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py index ee1472f..b7eee0c 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py +++ b/finecode_extension_runner/src/finecode_extension_runner/impls/project_info_provider.py @@ -1,10 +1,26 @@ -from typing import Any, Callable +import pathlib +from typing import Any, Awaitable, Callable from finecode_extension_api.interfaces import iprojectinfoprovider -project_raw_config_getter: Callable - class ProjectInfoProvider(iprojectinfoprovider.IProjectInfoProvider): - async def get_project_raw_config(self) -> dict[str, Any]: - return await project_raw_config_getter() + def __init__( + self, + project_def_path_getter: Callable[[], pathlib.Path], + project_raw_config_getter: Callable[[str], Awaitable[dict[str, Any]]], + ) -> None: + self.project_def_path_getter = project_def_path_getter + self.project_raw_config_getter = project_raw_config_getter + + def get_current_project_def_path(self) -> pathlib.Path: + return self.project_def_path_getter() + + async def get_project_raw_config( + self, project_def_path: pathlib.Path + ) -> dict[str, Any]: + return await self.project_raw_config_getter(str(project_def_path)) + + async def get_current_project_raw_config(self) -> dict[str, Any]: + current_project_path = self.get_current_project_def_path() + return await self.get_project_raw_config(project_def_path=current_project_path) diff --git a/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py index 98cec46..e6328d1 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py +++ b/finecode_extension_runner/src/finecode_extension_runner/lsp_server.py @@ -7,9 +7,11 @@ import atexit import dataclasses +import functools import json import pathlib import time +import typing import pygls.exceptions as pygls_exceptions from loguru import logger @@ -19,7 +21,6 @@ from finecode_extension_api import code_action from finecode_extension_runner import domain, schemas, services from finecode_extension_runner._services import run_action as run_action_service -from finecode_extension_runner.impls import project_info_provider class CustomLanguageServer(lsp_server.LanguageServer): @@ -66,53 +67,6 @@ def on_process_exit(): atexit.register(on_process_exit) - async def document_requester(uri: str): - try: - document = await server.protocol.send_request_async( - "documents/get", params={"uri": uri} - ) - except pygls_exceptions.JsonRpcInternalError as error: - if error.message == "Exception: Document is not opened": - raise domain.TextDocumentNotOpened() - else: - raise error - - return domain.TextDocumentInfo( - uri=document.uri, version=document.version, text=document.text - ) - - async def document_saver(uri: str, content: str): - document = await server.protocol.send_request_async( - "documents/get", params={"uri": uri} - ) - document_lines = document.text.split("\n") - params = types.ApplyWorkspaceEditParams( - edit=types.WorkspaceEdit( - # dict seems to be incorrectly unstructured on client(pygls issue?) - # use document_changes instead of changes - document_changes=[ - types.TextDocumentEdit( - text_document=types.OptionalVersionedTextDocumentIdentifier( - uri=uri - ), - edits=[ - types.TextEdit( - range=types.Range( - start=types.Position(line=0, character=0), - end=types.Position( - line=len(document_lines), - character=len(document_lines[-1]), - ), - ), - new_text=content, - ) - ], - ) - ] - ) - ) - await server.workspace_apply_edit_async(params) - def send_partial_result( token: int | str, partial_result: code_action.RunActionResult ) -> None: @@ -121,22 +75,8 @@ def send_partial_result( partial_result_json = json.dumps(partial_result_dict) server.progress(types.ProgressParams(token=token, value=partial_result_json)) - services.document_requester = document_requester - services.document_saver = document_saver run_action_service.set_partial_result_sender(send_partial_result) - async def get_project_raw_config(): - try: - raw_config = await server.protocol.send_request_async( - "projects/getRawConfig", params={} - ) - except pygls_exceptions.JsonRpcInternalError as error: - raise error - - return json.loads(raw_config.config) - - project_info_provider.project_raw_config_getter = get_project_raw_config - return server @@ -163,6 +103,68 @@ def _document_did_close( services.document_did_close(params.text_document.uri) +async def document_requester(server: lsp_server.LanguageServer, uri: str): + try: + document = await server.protocol.send_request_async( + "documents/get", params={"uri": uri} + ) + except pygls_exceptions.JsonRpcInternalError as error: + if error.message == "Exception: Document is not opened": + raise domain.TextDocumentNotOpened() + else: + raise error + + return domain.TextDocumentInfo( + uri=document.uri, version=document.version, text=document.text + ) + + +async def document_saver(server: lsp_server.LanguageServer, uri: str, content: str): + document = await server.protocol.send_request_async( + "documents/get", params={"uri": uri} + ) + document_lines = document.text.split("\n") + params = types.ApplyWorkspaceEditParams( + edit=types.WorkspaceEdit( + # dict seems to be incorrectly unstructured on client(pygls issue?) + # use document_changes instead of changes + document_changes=[ + types.TextDocumentEdit( + text_document=types.OptionalVersionedTextDocumentIdentifier( + uri=uri + ), + edits=[ + types.TextEdit( + range=types.Range( + start=types.Position(line=0, character=0), + end=types.Position( + line=len(document_lines), + character=len(document_lines[-1]), + ), + ), + new_text=content, + ) + ], + ) + ] + ) + ) + await server.workspace_apply_edit_async(params) + + +async def get_project_raw_config( + server: lsp_server.LanguageServer, project_def_path: str +) -> dict[str, typing.Any]: + try: + raw_config = await server.protocol.send_request_async( + "projects/getRawConfig", params={"projectDefPath": project_def_path} + ) + except pygls_exceptions.JsonRpcInternalError as error: + raise error + + return json.loads(raw_config.config) + + async def update_config(ls: lsp_server.LanguageServer, params): logger.trace(f"Update config: {params}") try: @@ -193,7 +195,12 @@ async def update_config(ls: lsp_server.LanguageServer, params): }, action_handler_configs=action_handler_configs, ) - response = await services.update_config(request=request) + response = await services.update_config( + request=request, + document_requester=functools.partial(document_requester, ls), + document_saver=functools.partial(document_saver, ls), + project_raw_config_getter=functools.partial(get_project_raw_config, ls), + ) return response.to_dict() except Exception as e: logger.exception(f"Update config error: {e}") diff --git a/finecode_extension_runner/src/finecode_extension_runner/services.py b/finecode_extension_runner/src/finecode_extension_runner/services.py index 290cdd3..86bb2cf 100644 --- a/finecode_extension_runner/src/finecode_extension_runner/services.py +++ b/finecode_extension_runner/src/finecode_extension_runner/services.py @@ -28,21 +28,14 @@ ) from finecode_extension_runner.di import bootstrap as di_bootstrap -document_requester: typing.Callable -document_saver: typing.Callable - - -async def get_document(uri: str): - doc = await document_requester(uri) - return doc - - -async def save_document(uri: str, content: str): - await document_saver(uri, content) - async def update_config( request: schemas.UpdateConfigRequest, + document_requester: typing.Callable, + document_saver: typing.Callable, + project_raw_config_getter: typing.Callable[ + [str], typing.Awaitable[dict[str, typing.Any]] + ], ) -> schemas.UpdateConfigResponse: project_path = Path(request.working_dir) @@ -76,8 +69,15 @@ async def update_config( # currently update_config is called only once directly after runner start. So we can # bootstrap here. Should be changed after adding updating configuration on the fly. + def project_def_path_getter() -> Path: + assert global_state.runner_context is not None + return global_state.runner_context.project.path + di_bootstrap.bootstrap( - get_document_func=get_document, save_document_func=save_document + get_document_func=document_requester, + save_document_func=document_saver, + project_def_path_getter=project_def_path_getter, + project_raw_config_getter=project_raw_config_getter, ) return schemas.UpdateConfigResponse() diff --git a/src/finecode/cli_app/dump_config.py b/src/finecode/cli_app/dump_config.py index a46b1ea..3e0742d 100644 --- a/src/finecode/cli_app/dump_config.py +++ b/src/finecode/cli_app/dump_config.py @@ -4,7 +4,7 @@ from loguru import logger from finecode import context, services -from finecode.config import read_configs, config_models +from finecode.config import config_models, read_configs from finecode.runner import manager as runner_manager @@ -27,7 +27,7 @@ async def dump_config(workdir_path: pathlib.Path, project_name: str): for project_dir_path, project in ws_context.ws_projects.items() if project.name == project_name } - + # read configs without presets, this is required to be able to start runners in # the next step for project in ws_context.ws_projects.values(): @@ -40,7 +40,6 @@ async def dump_config(workdir_path: pathlib.Path, project_name: str): f"Reading project configs(without presets) in {project.dir_path} failed: {exception.message}" ) - # start runner to init project config try: try: diff --git a/src/finecode/cli_app/prepare_envs.py b/src/finecode/cli_app/prepare_envs.py index 7ee3b54..bf535fd 100644 --- a/src/finecode/cli_app/prepare_envs.py +++ b/src/finecode/cli_app/prepare_envs.py @@ -263,7 +263,7 @@ async def check_or_recreate_all_dev_workspace_envs( raise PrepareEnvsFailed( f"'prepare_dev_workspaces_env' failed in {current_project.name}: {exception.message}" ) - + if action_result.return_code != 0: raise PrepareEnvsFailed( f"'prepare_dev_workspaces_env' ended in {current_project.name} with return code {action_result.return_code}: {action_result.result}" diff --git a/src/finecode/config/collect_actions.py b/src/finecode/config/collect_actions.py index 69a7339..06bf796 100644 --- a/src/finecode/config/collect_actions.py +++ b/src/finecode/config/collect_actions.py @@ -41,8 +41,10 @@ def _collect_action_handler_configs_in_config( action_handlers_configs = config["tool"]["finecode"].get("action_handler", []) action_handler_config_by_source: dict[str, dict[str, Any]] = {} for handler_def in action_handlers_configs: - if "source" not in handler_def or not isinstance(handler_def['source'], str): - raise config_models.ConfigurationError("Action handler definition expected to have a 'source' field(to identify handler) and it should be a string") + if "source" not in handler_def or not isinstance(handler_def["source"], str): + raise config_models.ConfigurationError( + "Action handler definition expected to have a 'source' field(to identify handler) and it should be a string" + ) handler_config = handler_def.get("config", None) if handler_config is not None: diff --git a/src/finecode/runner/manager.py b/src/finecode/runner/manager.py index ee7e6e6..a6c5903 100644 --- a/src/finecode/runner/manager.py +++ b/src/finecode/runner/manager.py @@ -141,9 +141,15 @@ async def on_progress(params: types.ProgressParams): register_progress_feature(on_progress) async def get_project_raw_config(params): - # assume raw config exists, because if runner is running, there is always a - # raw config - return {"config": json.dumps(ws_context.ws_projects_raw_configs[runner_dir])} + project_def_path_str = params.projectDefPath + project_def_path = Path(project_def_path_str) + try: + project_raw_config = ws_context.ws_projects_raw_configs[ + project_def_path.parent + ] + except KeyError: + raise ValueError(f"Config of project '{project_def_path_str}' not found") + return {"config": json.dumps(project_raw_config)} register_get_project_raw_config_feature = runner_info_instance.client.feature( "projects/getRawConfig" From 5a27d087682aec68af9529bec9fd814b34a321d2 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Wed, 27 Aug 2025 18:09:03 +0200 Subject: [PATCH 29/46] Move 'start_required_environments' in proxy_utils and handle exceptions in it --- src/finecode/cli_app/prepare_envs.py | 11 ++++- src/finecode/cli_app/run.py | 73 ++++------------------------ src/finecode/proxy_utils.py | 72 +++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 66 deletions(-) diff --git a/src/finecode/cli_app/prepare_envs.py b/src/finecode/cli_app/prepare_envs.py index bf535fd..cf62d7c 100644 --- a/src/finecode/cli_app/prepare_envs.py +++ b/src/finecode/cli_app/prepare_envs.py @@ -3,7 +3,7 @@ from loguru import logger -from finecode import context, domain, services +from finecode import context, domain, proxy_utils, services from finecode.cli_app import run as run_cli from finecode.config import collect_actions, config_models, read_configs from finecode.runner import manager as runner_manager @@ -84,7 +84,14 @@ async def prepare_envs(workdir_path: pathlib.Path, recreate: bool) -> None: # action payload can be kept empty because it will be filled in payload preprocessor action_payload: dict[str, str | bool] = {"recreate": recreate} - await run_cli.start_required_environments(actions_by_projects, ws_context) + try: + await proxy_utils.start_required_environments( + actions_by_projects, ws_context + ) + except proxy_utils.StartingEnvironmentsFailed as exception: + raise PrepareEnvsFailed( + f"Failed to start environments for running 'prepare_runners': {exception.message}" + ) try: (result_output, result_return_code) = ( diff --git a/src/finecode/cli_app/run.py b/src/finecode/cli_app/run.py index 68d735e..f5be9c4 100644 --- a/src/finecode/cli_app/run.py +++ b/src/finecode/cli_app/run.py @@ -6,7 +6,7 @@ import ordered_set from loguru import logger -from finecode import context, domain, services +from finecode import context, domain, proxy_utils, services from finecode.config import collect_actions, config_models, read_configs from finecode.runner import manager as runner_manager from finecode.runner import runner_info @@ -17,66 +17,6 @@ def __init__(self, message: str) -> None: self.message = message -async def start_required_environments( - actions_by_projects: dict[pathlib.Path, list[str]], - ws_context: context.WorkspaceContext, - update_config_in_running_runners: bool = False, -) -> None: - """Collect all required envs from actions that will be run and start them.""" - required_envs_by_project: dict[pathlib.Path, set[str]] = {} - for project_dir_path, action_names in actions_by_projects.items(): - project = ws_context.ws_projects[project_dir_path] - if project.actions is not None: - project_required_envs = set() - for action_name in action_names: - # find the action and collect envs from its handlers - action = next( - (a for a in project.actions if a.name == action_name), None - ) - if action is not None: - for handler in action.handlers: - project_required_envs.add(handler.env) - required_envs_by_project[project_dir_path] = project_required_envs - - # start runners for required environments that aren't already running - for project_dir_path, required_envs in required_envs_by_project.items(): - project = ws_context.ws_projects[project_dir_path] - existing_runners = ws_context.ws_projects_extension_runners.get( - project_dir_path, {} - ) - - for env_name in required_envs: - runner_exist = env_name in existing_runners - start_runner = True - if runner_exist: - runner_is_running = ( - existing_runners[env_name].status - == runner_info.RunnerStatus.RUNNING - ) - start_runner = not runner_is_running - - if start_runner: - try: - runner = await runner_manager.start_runner( - project_def=project, env_name=env_name, ws_context=ws_context - ) - except Exception as e: - logger.warning( - f"Failed to start runner for env '{env_name}' in project '{project.name}': {e}" - ) - # TODO: raise error - else: - if update_config_in_running_runners: - runner = existing_runners[env_name] - logger.trace( - f"Runner {runner.working_dir_path} {runner.env_name} is running already, update config" - ) - # TODO: handle errors - await runner_manager.update_runner_config( - runner=runner, project=project - ) - - async def run_actions( workdir_path: pathlib.Path, projects_names: list[str] | None, @@ -217,9 +157,14 @@ async def run_actions( # actions will be run in all projects inside actions_by_projects = find_projects_with_actions(ws_context, actions) - await start_required_environments( - actions_by_projects, ws_context, update_config_in_running_runners=True - ) + try: + await proxy_utils.start_required_environments( + actions_by_projects, ws_context, update_config_in_running_runners=True + ) + except proxy_utils.StartingEnvironmentsFailed as exception: + raise RunFailed( + f"Failed to start environments for running actions: {exception.message}" + ) return await run_actions_in_all_projects( actions_by_projects, action_payload, ws_context, concurrently diff --git a/src/finecode/proxy_utils.py b/src/finecode/proxy_utils.py index 4dcf9d9..eedf32f 100644 --- a/src/finecode/proxy_utils.py +++ b/src/finecode/proxy_utils.py @@ -10,6 +10,7 @@ from finecode import context, domain, find_project, services from finecode.runner import manager as runner_manager from finecode.runner import runner_client, runner_info +from finecode.runner.manager import RunnerFailedToStart from finecode.services import ActionRunFailed @@ -259,10 +260,81 @@ def find_all_projects_with_action( return relevant_projects_paths +class StartingEnvironmentsFailed(Exception): + def __init__(self, message: str) -> None: + self.message = message + + +async def start_required_environments( + actions_by_projects: dict[pathlib.Path, list[str]], + ws_context: context.WorkspaceContext, + update_config_in_running_runners: bool = False, +) -> None: + """Collect all required envs from actions that will be run and start them.""" + required_envs_by_project: dict[pathlib.Path, set[str]] = {} + for project_dir_path, action_names in actions_by_projects.items(): + project = ws_context.ws_projects[project_dir_path] + if project.actions is not None: + project_required_envs = set() + for action_name in action_names: + # find the action and collect envs from its handlers + action = next( + (a for a in project.actions if a.name == action_name), None + ) + if action is not None: + for handler in action.handlers: + project_required_envs.add(handler.env) + required_envs_by_project[project_dir_path] = project_required_envs + + # start runners for required environments that aren't already running + for project_dir_path, required_envs in required_envs_by_project.items(): + project = ws_context.ws_projects[project_dir_path] + existing_runners = ws_context.ws_projects_extension_runners.get( + project_dir_path, {} + ) + + for env_name in required_envs: + runner_exist = env_name in existing_runners + start_runner = True + if runner_exist: + runner_is_running = ( + existing_runners[env_name].status + == runner_info.RunnerStatus.RUNNING + ) + start_runner = not runner_is_running + + if start_runner: + try: + runner = await runner_manager.start_runner( + project_def=project, env_name=env_name, ws_context=ws_context + ) + except runner_manager.RunnerFailedToStart as e: + raise StartingEnvironmentsFailed( + f"Failed to start runner for env '{env_name}' in project '{project.name}': {e}" + ) + else: + if update_config_in_running_runners: + runner = existing_runners[env_name] + logger.trace( + f"Runner {runner.working_dir_path} {runner.env_name} is running already, update config" + ) + + try: + await runner_manager.update_runner_config( + runner=runner, project=project + ) + except RunnerFailedToStart as exception: + raise StartingEnvironmentsFailed( + f"Failed to update config of runner {runner.working_dir_path} {runner.env_name}" + ) + + __all__ = [ "find_action_project_and_run", "find_action_project_and_run_with_partial_results", "run_with_partial_results", # reexport for easier use of proxy helpers "ActionRunFailed", + "start_required_environments", + "StartingEnvironmentsFailed", ] From 88d6d416706f4a9c88b03e4bf3829224d3702441 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 07:31:42 +0200 Subject: [PATCH 30/46] finecode_extension_api v0.3.0 --- finecode_extension_api/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finecode_extension_api/pyproject.toml b/finecode_extension_api/pyproject.toml index 82081c6..326575c 100644 --- a/finecode_extension_api/pyproject.toml +++ b/finecode_extension_api/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "finecode-extension-api" -version = "0.2.0" +version = "0.3.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" From 1f9ad93f8357b76021993ad9574ea335dc3168b3 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 07:35:11 +0200 Subject: [PATCH 31/46] fine_python_ast v0.2.0 --- extensions/fine_python_ast/pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/fine_python_ast/pyproject.toml b/extensions/fine_python_ast/pyproject.toml index 0e03fb3..1e76cf6 100644 --- a/extensions/fine_python_ast/pyproject.toml +++ b/extensions/fine_python_ast/pyproject.toml @@ -1,8 +1,8 @@ [project] name = "fine-python-ast" -version = "0.1.1" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.3"] +dependencies = ["finecode_extension_api==0.3.*"] From 596fb1881e340ff6a90c96de902f23863719efa7 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 07:42:22 +0200 Subject: [PATCH 32/46] fine_python_black v0.2.0 --- extensions/fine_python_black/pyproject.toml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/extensions/fine_python_black/pyproject.toml b/extensions/fine_python_black/pyproject.toml index 2b79dfe..779196f 100644 --- a/extensions/fine_python_black/pyproject.toml +++ b/extensions/fine_python_black/pyproject.toml @@ -1,13 +1,8 @@ [project] name = "fine_python_black" -version = "0.1.0" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.0", "black (>=25.1.0,<26.0.0)"] - - -[build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +dependencies = ["finecode_extension_api==0.3.*", "black (>=25.1.0,<26.0.0)"] From 5aba5ec7b61ea1503a991502d6933c629788f8cb Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 07:57:20 +0200 Subject: [PATCH 33/46] fine_python_flake8 v0.2.0 --- extensions/fine_python_flake8/pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/fine_python_flake8/pyproject.toml b/extensions/fine_python_flake8/pyproject.toml index e91def9..4abbac0 100644 --- a/extensions/fine_python_flake8/pyproject.toml +++ b/extensions/fine_python_flake8/pyproject.toml @@ -1,13 +1,13 @@ [project] name = "fine-python-flake8" -version = "0.1.0" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" dependencies = [ - "finecode_extension_api==0.1.*", - "fine_python_ast==0.1.*", + "finecode_extension_api==0.3.*", + "fine_python_ast==0.2.*", "types-flake8 (>=7.1.0.20241020,<8.0.0.0)", "flake8 (>=7.1.2,<8.0.0)", ] From 7e7ed4f4b8fd38fd438b6f9973efba39acb71bc5 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 08:04:21 +0200 Subject: [PATCH 34/46] fine_python_isort v0.2.0 --- extensions/fine_python_isort/pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/fine_python_isort/pyproject.toml b/extensions/fine_python_isort/pyproject.toml index 6eadc9b..b6ef046 100644 --- a/extensions/fine_python_isort/pyproject.toml +++ b/extensions/fine_python_isort/pyproject.toml @@ -1,8 +1,8 @@ [project] name = "fine-python-isort" -version = "0.1.0" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">= 3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.*", "isort (>=5.13, <6)"] +dependencies = ["finecode_extension_api==0.3.*", "isort (>=5.13, <6)"] From c37e1b73b380a88ce99a38dedbaf9eb3a0e5cf80 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 08:06:11 +0200 Subject: [PATCH 35/46] fine_python_mypy v0.2.0 --- .../fine_python_mypy/fine_python_mypy/ast_provider.py | 6 +++--- extensions/fine_python_mypy/pyproject.toml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/fine_python_mypy/fine_python_mypy/ast_provider.py b/extensions/fine_python_mypy/fine_python_mypy/ast_provider.py index fe5d47e..d81fcfc 100644 --- a/extensions/fine_python_mypy/fine_python_mypy/ast_provider.py +++ b/extensions/fine_python_mypy/fine_python_mypy/ast_provider.py @@ -1,14 +1,14 @@ # import time from pathlib import Path -import mypy.nodes as mypy_nodes import mypy.build as mypy_build import mypy.modulefinder as modulefinder +import mypy.nodes as mypy_nodes import mypy.options as mypy_options -from finecode_extension_api.interfaces import ifilemanager, icache, ilogger - from fine_python_mypy import iast_provider +from finecode_extension_api.interfaces import icache, ifilemanager, ilogger + class MypySingleAstProvider(iast_provider.IMypySingleAstProvider): CACHE_KEY = "MypySingleAstProvider" diff --git a/extensions/fine_python_mypy/pyproject.toml b/extensions/fine_python_mypy/pyproject.toml index 761c79f..346bb29 100644 --- a/extensions/fine_python_mypy/pyproject.toml +++ b/extensions/fine_python_mypy/pyproject.toml @@ -1,8 +1,8 @@ [project] name = "fine-python-mypy" -version = "0.1.0" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.*", "mypy (>=1.15, <2.0)"] +dependencies = ["finecode_extension_api==0.3.*", "mypy (>=1.15, <2.0)"] From 9a657c41f3025c52fcb95e8ccec5230472e50ec6 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 08:10:51 +0200 Subject: [PATCH 36/46] fine_python_pip v0.1.0 --- extensions/fine_python_pip/pyproject.toml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/extensions/fine_python_pip/pyproject.toml b/extensions/fine_python_pip/pyproject.toml index 423a57c..54e3651 100644 --- a/extensions/fine_python_pip/pyproject.toml +++ b/extensions/fine_python_pip/pyproject.toml @@ -1,21 +1,18 @@ [project] name = "fine-python-pip" -version = "0.1.1" +version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.3"] +dependencies = ["finecode_extension_api==0.3.*"] [dependency-groups] -dev_workspace = ["finecode==0.2.6.dev14+g790c37afd.d20250821"] +dev_workspace = ["finecode==0.3.0"] dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] [tool.finecode] presets = [{ source = "finecode_dev_common_preset" }] -[tool.finecode.env.runtime.dependencies] -finecode_extension_api = { path = "../../finecode_extension_api", editable = true } - [tool.finecode.env.dev_no_runtime.dependencies] finecode_dev_common_preset = { path = "../../finecode_dev_common_preset", editable = true } From 87937c5b76f9fa0c5855a2a9bffb647c45c5a659 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 08:11:41 +0200 Subject: [PATCH 37/46] fine_python_virtualenv v0.1.0 --- extensions/fine_python_virtualenv/pyproject.toml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/extensions/fine_python_virtualenv/pyproject.toml b/extensions/fine_python_virtualenv/pyproject.toml index 0876960..4c018a7 100644 --- a/extensions/fine_python_virtualenv/pyproject.toml +++ b/extensions/fine_python_virtualenv/pyproject.toml @@ -1,24 +1,21 @@ [project] name = "fine-python-virtualenv" -version = "0.1.2" +version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" dependencies = [ - "finecode_extension_api==0.1.3", + "finecode_extension_api==0.3.*", "virtualenv (>=20.0.0,<21.0.0)", ] [dependency-groups] -dev_workspace = ["finecode==0.2.6.dev14+g790c37afd.d20250821"] +dev_workspace = ["finecode==0.3.0"] dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] [tool.finecode] presets = [{ source = "finecode_dev_common_preset" }] -[tool.finecode.env.runtime.dependencies] -finecode_extension_api = { path = "../../finecode_extension_api", editable = true } - [tool.finecode.env.dev_no_runtime.dependencies] finecode_dev_common_preset = { path = "../../finecode_dev_common_preset", editable = true } From cffb1fb3314af5b09f3fe3981f52684eb6529a4d Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 08:19:24 +0200 Subject: [PATCH 38/46] fine_python_format v0.2.0 --- .../fine_python_format/fine_python_format/preset.toml | 11 ++++++----- presets/fine_python_format/pyproject.toml | 9 ++------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/presets/fine_python_format/fine_python_format/preset.toml b/presets/fine_python_format/fine_python_format/preset.toml index ec2babd..9a660cb 100644 --- a/presets/fine_python_format/fine_python_format/preset.toml +++ b/presets/fine_python_format/fine_python_format/preset.toml @@ -2,17 +2,18 @@ source = "finecode_extension_api.actions.format.FormatAction" handlers = [ { name = "isort", source = "fine_python_isort.IsortFormatHandler", env = "dev_no_runtime", dependencies = [ - "fine_python_black==0.1.0", + "fine_python_black==0.2.*", ] }, { name = "black", source = "fine_python_black.BlackFormatHandler", env = "dev_no_runtime", dependencies = [ - "fine_python_isort==0.1.0", + "fine_python_isort==0.2.*", ] }, { name = "save", source = "finecode_extension_api.actions.format.SaveFormatHandler", env = "dev_no_runtime", dependencies = [ - "finecode_extension_api==0.1.0", + "finecode_extension_api==0.3.*", ] }, ] -[tool.finecode.action_handler.isort.config] +[[tool.finecode.action_handler]] +source = "fine_python_isort.IsortFormatHandler" # make isort config compatible with black # see https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#isort -profile = "black" +config.profile = "black" diff --git a/presets/fine_python_format/pyproject.toml b/presets/fine_python_format/pyproject.toml index 025ec95..5d99042 100644 --- a/presets/fine_python_format/pyproject.toml +++ b/presets/fine_python_format/pyproject.toml @@ -1,16 +1,11 @@ [project] name = "fine-python-format" -version = "0.1.0" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.*"] - -# TODO: rework -# [tool.poetry.group.dev.dependencies] -# finecode = { version = "0.2.0" } -# finecode_dev_common_preset = { path = "../../finecode_dev_common_preset", develop = true } +dependencies = ["finecode_extension_api==0.3.*"] [build-system] requires = ["setuptools>=64"] From 46179ca43cc41956acb91c6e4b9d1a01b7572292 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 08:21:28 +0200 Subject: [PATCH 39/46] fine_python_lint v0.2.0 --- .../fine_python_lint/preset.toml | 13 +++++++------ presets/fine_python_lint/pyproject.toml | 19 +++++++------------ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/presets/fine_python_lint/fine_python_lint/preset.toml b/presets/fine_python_lint/fine_python_lint/preset.toml index 978178e..0c97b72 100644 --- a/presets/fine_python_lint/fine_python_lint/preset.toml +++ b/presets/fine_python_lint/fine_python_lint/preset.toml @@ -2,17 +2,18 @@ source = "finecode_extension_api.actions.lint.LintAction" handlers = [ { name = "flake8", source = "fine_python_flake8.Flake8LintHandler", env = "dev_no_runtime", dependencies = [ - "fine_python_flake8==0.1.0", + "fine_python_flake8==0.2.*", "flake8-bugbear (>=24.12.12,<25.0.0)", ] }, { name = "mypy", source = "fine_python_mypy.MypyLintHandler", env = "dev_no_runtime", dependencies = [ - "fine_python_mypy==0.1.0", + "fine_python_mypy==0.2.*", ] }, ] -[tool.finecode.action_handler.flake8.config] -max_line_length = 80 -extend_select = ["B950"] +[[tool.finecode.action_handler]] +source = "fine_python_flake8.Flake8LintHandler" +config.max_line_length = 80 +config.extend_select = ["B950"] # W391 is not compatible with black, because black adds an empty line to the end of the file # TODO: move in recommended config once config merging is implemented -extend_ignore = ["E203", "E501", "E701", "W391"] +config.extend_ignore = ["E203", "E501", "E701", "W391"] diff --git a/presets/fine_python_lint/pyproject.toml b/presets/fine_python_lint/pyproject.toml index 4cd9e54..ead7668 100644 --- a/presets/fine_python_lint/pyproject.toml +++ b/presets/fine_python_lint/pyproject.toml @@ -1,20 +1,15 @@ [project] name = "fine-python-lint" -version = "0.1.0" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = [ - "fine_python_mypy==0.1.0", - "fine_python_flake8==0.1.0", - "flake8-bugbear (>=24.12.12,<25.0.0)", -] - -[tool.poetry.group.dev.dependencies] -finecode = { version = "0.2.0" } -finecode_dev_common_preset = { path = "../../finecode_dev_common_preset", develop = true } +dependencies = ["finecode_extension_api==0.3.*"] [build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +requires = ["setuptools>=64"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.package-data] +fine_python_lint = ["preset.toml"] From 176e14a37792cab60b204237a5ff4607187f33ba Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 08:25:07 +0200 Subject: [PATCH 40/46] fine_python_recommended v0.2.0 --- presets/fine_python_recommended/pyproject.toml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/presets/fine_python_recommended/pyproject.toml b/presets/fine_python_recommended/pyproject.toml index 099c5b4..30cd230 100644 --- a/presets/fine_python_recommended/pyproject.toml +++ b/presets/fine_python_recommended/pyproject.toml @@ -1,16 +1,15 @@ [project] name = "fine-python-recommended" -version = "0.1.0" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" -dependencies = ["fine_python_format==0.1.0", "fine_python_lint==0.1.0"] - -[tool.poetry.group.dev.dependencies] -finecode = { version = "0.2.0" } -finecode_dev_common_preset = { path = "../../finecode_dev_common_preset", develop = true } +dependencies = ["fine_python_format==0.2.*", "fine_python_lint==0.2.*"] [build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +requires = ["setuptools>=64"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.package-data] +fine_python_recommended = ["preset.toml"] From 27314dece11cbebc023830c9bba4dab225960a2e Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 12:12:34 +0200 Subject: [PATCH 41/46] fine_python_module_exports v0.2.0 --- .../fine_python_module_exports/api.py | 118 ++++++++++++++++++ .../fine_python_module_exports/extension.py | 69 ++++++++++ .../fine_python_module_exports/pyproject.toml | 4 +- .../tests/__init__.py | 0 4 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 extensions/fine_python_module_exports/fine_python_module_exports/api.py create mode 100644 extensions/fine_python_module_exports/fine_python_module_exports/extension.py create mode 100644 extensions/fine_python_module_exports/tests/__init__.py diff --git a/extensions/fine_python_module_exports/fine_python_module_exports/api.py b/extensions/fine_python_module_exports/fine_python_module_exports/api.py new file mode 100644 index 0000000..9bd8fd7 --- /dev/null +++ b/extensions/fine_python_module_exports/fine_python_module_exports/api.py @@ -0,0 +1,118 @@ +""" +Here we don't validate file, we extract only valid data. Linter is responsible for +validation but we need to be aware of invalid data. +""" + +import ast +import enum + +from finecode_extension_api import common_types + + +def find_exported_members_names(module_ast: ast.Module) -> list[str] | None: + # if returns None, then there are no explicit exports + exports_found: bool = False + exported_members_names: list[str] = [] + + # '__all__' is usually at the end of file, iterate from the end + for stmt in module_ast.body[::-1]: + if isinstance(stmt, ast.Assign): + assign_target = stmt.targets[0] + if isinstance(assign_target, ast.Name) and assign_target.id == "__all__": + exports_found = True + rvalue = stmt.value + if isinstance(rvalue, ast.List): + exported_members_names = [ + item.value + for item in rvalue.elts + if isinstance(item, ast.Constant) + and isinstance(item.value, str) + ] + break + + if not exports_found: + return None + return exported_members_names + + +class ModuleMemberAccessType(enum.Enum): + PUBLIC = enum.auto() + PRIVATE = enum.auto() + # some statements like imports have no access type currently + UNKNOWN = enum.auto() + + +class PositionRelativeRange(enum.Enum): + BEFORE = enum.auto() + INSIDE = enum.auto() + AFTER = enum.auto() + + +def get_stmt_position_relative_range( + stmt: ast.stmt, range_in_doc: common_types.Range +) -> PositionRelativeRange: + # check start position of statement relative to range. Note that end of statement + # if it is multiline can be in range even with PositionRelativeRange.BEFORE. + if stmt.lineno < range_in_doc.start.line: + return PositionRelativeRange.BEFORE + elif stmt.lineno > range_in_doc.end.line: + return PositionRelativeRange.AFTER + else: + # if statement is at the first line of at the last line of range, check also + # column + if stmt.lineno == range_in_doc.start.line: + if stmt.col_offset < range_in_doc.start.character: + return PositionRelativeRange.BEFORE + else: + return PositionRelativeRange.INSIDE + elif stmt.lineno == range_in_doc.end.line: + if stmt.col_offset > range_in_doc.end.character: + return PositionRelativeRange.BEFORE + else: + return PositionRelativeRange.INSIDE + else: + return PositionRelativeRange.INSIDE + + +def get_module_members_with_access_type( + module_ast: ast.Module, + exported_members_names: list[str] | None, + range_in_doc: common_types.Range | None, +) -> dict[ast.stmt, ModuleMemberAccessType]: + module_members_with_access_type: dict[ast.stmt, ModuleMemberAccessType] = {} + default_stmt_access_type: ModuleMemberAccessType = ( + ModuleMemberAccessType.PRIVATE + if exported_members_names is not None + else ModuleMemberAccessType.PUBLIC + ) + + for stmt in module_ast.body: + stmt_name: str = "" + + if range_in_doc is not None: + relative_position: PositionRelativeRange = get_stmt_position_relative_range( + stmt, range_in_doc + ) + if relative_position == PositionRelativeRange.BEFORE: + continue + elif relative_position == PositionRelativeRange.AFTER: + break + + match stmt: + case ast.FunctionDef() | ast.AsyncFunctionDef() | ast.ClassDef(): + stmt_name = stmt.name + # TODO: assignment + # TODO: import? <- possible improvement in future + case _: + continue + + stmt_access_type: ModuleMemberAccessType = default_stmt_access_type + if exported_members_names is not None: + stmt_access_type = ( + ModuleMemberAccessType.PUBLIC + if stmt_name in exported_members_names + else ModuleMemberAccessType.PRIVATE + ) + module_members_with_access_type[stmt] = stmt_access_type + + return module_members_with_access_type diff --git a/extensions/fine_python_module_exports/fine_python_module_exports/extension.py b/extensions/fine_python_module_exports/fine_python_module_exports/extension.py new file mode 100644 index 0000000..5d35f83 --- /dev/null +++ b/extensions/fine_python_module_exports/fine_python_module_exports/extension.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +import ast +from pathlib import Path + +from fine_python_ast import iast_provider +from fine_python_module_exports import api + +from finecode_extension_api import common_types +from finecode_extension_api.actions.ide import text_document_inlay_hint + +# from finecode_extension_api import code_action + +# from finecode.extension_runner.interfaces import icache + +def uri_str_to_path(uri_str: str) -> Path: + return Path(uri_str.replace("file://", "")) + + +async def get_document_inlay_hints( + payload: text_document_inlay_hint.InlayHintPayload, + ast_provider: iast_provider.IPythonSingleAstProvider, + # cache: icache.ICache +) -> text_document_inlay_hint.InlayHintResult: + """ + ~~It's cheap enough to calculate access type for the whole file, so calculate and + cache. Then get nodes and access types for asked range.~~ + """ + # use uri? + file_path: Path = uri_str_to_path(payload.text_document.uri) + try: + module_ast: ast.Module = await ast_provider.get_file_ast(file_path=file_path) + except SyntaxError: + return text_document_inlay_hint.InlayHintResult(hints=[]) + # mypy_file_revision: str = ast_provider.get_ast_revision(file_ast=mypy_file) + + # try: + # # cache.get_file_cache(file_path, mypy_file_revision) + # ... # TODO: get from cache + # except icache.CacheMissException: + + exported_members_names: list[str] | None = api.find_exported_members_names( + module_ast=module_ast + ) + module_members_with_access_type = api.get_module_members_with_access_type( + module_ast, exported_members_names, range_in_doc=payload.range + ) + + return text_document_inlay_hint.InlayHintResult( + hints=[ + text_document_inlay_hint.InlayHint( + position=common_types.Position( + line=stmt.lineno, character=stmt.col_offset + ), + label=( + "private" + if access_level == api.ModuleMemberAccessType.PRIVATE + else "public" + ), + kind=text_document_inlay_hint.InlayHintKind.TYPE, + padding_right=True, + ) + for stmt, access_level in module_members_with_access_type.items() + if access_level != api.ModuleMemberAccessType.UNKNOWN + ] + ) + + +def get_document_code_actions(): ... diff --git a/extensions/fine_python_module_exports/pyproject.toml b/extensions/fine_python_module_exports/pyproject.toml index 1d1ffe5..0d163c5 100644 --- a/extensions/fine_python_module_exports/pyproject.toml +++ b/extensions/fine_python_module_exports/pyproject.toml @@ -1,8 +1,8 @@ [project] name = "fine-python-module-exports" -version = "0.1.1" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">= 3.11, < 3.14" -dependencies = ["finecode_extension_api==0.1.3", "fine_python_ast==0.1.1"] +dependencies = ["finecode_extension_api==0.3.*", "fine_python_ast==0.2.*"] diff --git a/extensions/fine_python_module_exports/tests/__init__.py b/extensions/fine_python_module_exports/tests/__init__.py new file mode 100644 index 0000000..e69de29 From 220127f3883eaaebf745b32eaa8f22ee09604714 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 12:14:53 +0200 Subject: [PATCH 42/46] Fix passing nested action payload as json in CLI run command --- src/finecode/cli.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/finecode/cli.py b/src/finecode/cli.py index b8c5989..3d23742 100644 --- a/src/finecode/cli.py +++ b/src/finecode/cli.py @@ -1,6 +1,8 @@ import asyncio +import json import os import pathlib +import typing import sys import click @@ -84,6 +86,20 @@ async def show_user_message(message: str, message_type: str) -> None: ... +def deserialize_action_payload(raw_payload: dict[str, str]) -> dict[str, typing.Any]: + deserialized_payload = {} + for key, value in raw_payload.items(): + if value.startswith('{') and value.endswith('}'): + try: + deserialized_value = json.loads(value) + except json.JSONDecodeError: + deserialized_value = value + else: + deserialized_value = value + deserialized_payload[key] = deserialized_value + return deserialized_payload + + @cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True)) @click.pass_context def run(ctx) -> None: @@ -135,7 +151,7 @@ def run(ctx) -> None: sys.exit(1) # action payload - action_payload: dict[str, str] = {} + action_payload: dict[str, typing.Any] = {} for arg in args[processed_args_count:]: if not arg.startswith("--"): click.echo( @@ -152,15 +168,17 @@ def run(ctx) -> None: sys.exit(1) else: arg_name, arg_value = arg[2:].split("=") - action_payload[arg_name] = arg_value + arg_name = arg_name.replace('-', '_') + action_payload[arg_name] = arg_value.strip('"').strip("'") processed_args_count += 1 user_messages._notification_sender = show_user_message + deserialized_payload= deserialize_action_payload(action_payload) try: output, return_code = asyncio.run( run_cmd.run_actions( - workdir_path, projects, actions_to_run, action_payload, concurrently + workdir_path, projects, actions_to_run, deserialized_payload, concurrently ) ) click.echo(output) From b8cb767fe22d51918aa2c6327ee0fa5618c34b57 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 12:54:52 +0200 Subject: [PATCH 43/46] Finish updating versions --- extensions/fine_python_pip/pyproject.toml | 2 +- .../fine_python_virtualenv/pyproject.toml | 2 +- finecode_dev_common_preset/pyproject.toml | 12 ++--- .../finecode_dev_common_preset/preset.toml | 5 ++- finecode_extension_api/pyproject.toml | 2 +- finecode_extension_runner/pyproject.toml | 13 ++---- pyproject.toml | 44 +++++++++---------- src/finecode/base_config.toml | 6 +-- 8 files changed, 37 insertions(+), 49 deletions(-) diff --git a/extensions/fine_python_pip/pyproject.toml b/extensions/fine_python_pip/pyproject.toml index 54e3651..6751b15 100644 --- a/extensions/fine_python_pip/pyproject.toml +++ b/extensions/fine_python_pip/pyproject.toml @@ -9,7 +9,7 @@ dependencies = ["finecode_extension_api==0.3.*"] [dependency-groups] dev_workspace = ["finecode==0.3.0"] -dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] +dev_no_runtime = ["finecode_dev_common_preset==0.2.*"] [tool.finecode] presets = [{ source = "finecode_dev_common_preset" }] diff --git a/extensions/fine_python_virtualenv/pyproject.toml b/extensions/fine_python_virtualenv/pyproject.toml index 4c018a7..a0de105 100644 --- a/extensions/fine_python_virtualenv/pyproject.toml +++ b/extensions/fine_python_virtualenv/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ [dependency-groups] dev_workspace = ["finecode==0.3.0"] -dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] +dev_no_runtime = ["finecode_dev_common_preset==0.2.*"] [tool.finecode] presets = [{ source = "finecode_dev_common_preset" }] diff --git a/finecode_dev_common_preset/pyproject.toml b/finecode_dev_common_preset/pyproject.toml index ec45326..7424a97 100644 --- a/finecode_dev_common_preset/pyproject.toml +++ b/finecode_dev_common_preset/pyproject.toml @@ -1,18 +1,14 @@ [project] name = "finecode-dev-common-preset" -version = "0.1.0" +version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" requires-python = ">=3.11, < 3.14" dependencies = [ "fine_python_aksem @ git+https://github.com/Aksem/fine_python_aksem.git", - "fine_python_recommended==0.1.0", + "fine_python_recommended==0.2.*", ] -[tool.poetry] -packages = [{ include = "finecode_dev_common_preset", from = "src" }] - -[build-system] -requires = ["poetry-core>=2.0.0,<3.0.0"] -build-backend = "poetry.core.masonry.api" +[tool.setuptools.package-data] +finecode_dev_common_preset = ["preset.toml"] diff --git a/finecode_dev_common_preset/src/finecode_dev_common_preset/preset.toml b/finecode_dev_common_preset/src/finecode_dev_common_preset/preset.toml index 1c79174..3790b5f 100644 --- a/finecode_dev_common_preset/src/finecode_dev_common_preset/preset.toml +++ b/finecode_dev_common_preset/src/finecode_dev_common_preset/preset.toml @@ -4,8 +4,9 @@ presets = [ { source = "fine_python_aksem" }, ] -[tool.finecode.action_handler.black.config] -preview = true +[[tool.finecode.action_handler]] +source = "fine_python_black.BlackFormatHandler" +config.preview = true # currently, all packages in finecode repository are pure python packages, reuse # setuptools build in all of them diff --git a/finecode_extension_api/pyproject.toml b/finecode_extension_api/pyproject.toml index 326575c..900c6fc 100644 --- a/finecode_extension_api/pyproject.toml +++ b/finecode_extension_api/pyproject.toml @@ -9,7 +9,7 @@ dependencies = ["typing-extensions (>=4.12.2,<5.0.0)"] [dependency-groups] dev_workspace = ["finecode==0.3.0"] -dev_no_runtime = ["finecode_dev_common_preset==0.1.*"] +dev_no_runtime = ["finecode_dev_common_preset==0.2.*"] [tool.finecode] presets = [{ source = "finecode_dev_common_preset" }] diff --git a/finecode_extension_runner/pyproject.toml b/finecode_extension_runner/pyproject.toml index cb6984e..2676c37 100644 --- a/finecode_extension_runner/pyproject.toml +++ b/finecode_extension_runner/pyproject.toml @@ -12,17 +12,15 @@ dependencies = [ "pydantic==2.10.*", "platformdirs==4.3.*", "pygls==2.0.0-a2", - "finecode_extension_api==0.1.3", + "finecode_extension_api==0.3.*", + "deepmerge==2.0.*", ] [dependency-groups] -dev_workspace = [ - "build==1.2.2.post1", - "finecode==0.2.6.dev14+g790c37afd.d20250821", -] +dev_workspace = ["build==1.2.2.post1", "finecode==0.3.0"] dev = [{ include-group = "runtime" }, "pytest==7.4.*", "debugpy==1.8.*"] dev_no_runtime = [ - "finecode_dev_common_preset==0.1.*", + "finecode_dev_common_preset==0.2.*", # file_python_import_linter is temporary disabled, because it isn't ported to the new finecode_extension_api yet # "fine_python_import_linter @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_import_linter", ] @@ -34,9 +32,6 @@ finecode_dev_common_preset = { path = "../finecode_dev_common_preset", editable requires = ["setuptools>=64", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" -[tool.poetry] -packages = [{ include = "finecode_extension_runner", from = "src" }] - [tool.finecode] presets = [{ source = "finecode_dev_common_preset" }] diff --git a/pyproject.toml b/pyproject.toml index 9cd9e01..2230c04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,33 +13,35 @@ dependencies = [ "pydantic==2.10.*", "platformdirs==4.3.*", "pygls==2.0.0-a2", - "finecode_extension_api==0.1.0", + "finecode_extension_api==0.3.*", + "finecode_extension_runner==0.3.*", "ordered-set==4.1.*", "mcp==1.9.*", - "fine_python_virtualenv @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_virtualenv", - "fine_python_pip @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_pip", + "fine_python_virtualenv==0.1.*", + "fine_python_pip==0.1.*", ] [dependency-groups] -dev_workspace = ["build==1.2.2.post1", "finecode==0.2.*"] +dev_workspace = ["build==1.2.2.post1", "finecode==0.3.0", "debugpy==1.8.*"] dev = [{ include-group = "runtime" }, "pytest==7.4.*", "debugpy==1.8.*"] dev_no_runtime = [ - "fine_python_import_linter @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_import_linter", + "finecode_dev_common_preset==0.2.*", + # file_python_import_linter is temporary disabled, because it isn't ported to the new finecode_extension_api yet + # "fine_python_import_linter @ git+https://github.com/finecode-dev/finecode.git#subdirectory=extensions/fine_python_import_linter", ] [build-system] requires = ["setuptools>=64", "setuptools-scm>=8"] build-backend = "setuptools.build_meta" -[tool.poetry] -packages = [{ include = "finecode", from = "src" }] - -# TODO: intall finecode_dev_common_preset as a dependency from local path using finecode? # TODO: add import linter handler to lint action [tool.finecode] presets = [{ source = "finecode_dev_common_preset" }] +[tool.finecode.env.dev_no_runtime.dependencies] +finecode_dev_common_preset = { path = "./finecode_dev_common_preset", editable = true } + [tool.importlinter] root_package = "finecode" include_external_packages = true @@ -49,20 +51,11 @@ id = "wm-layered" name = "WM layered architecture" type = "layers" layers = [ - "finecode.workspace_manager.lsp_server.lsp_server", - "finecode.workspace_manager.lsp_server.services", - "finecode.workspace_manager.domain", + "finecode.lsp_server.lsp_server", + "finecode.lsp_server.services", + "finecode.domain", ] -[[tool.importlinter.contracts]] -id = "er-layered" -name = "ER layered architecture" -type = "layers" -layers = [ - "finecode.extension_runner.lsp_server", - "finecode.extension_runner.services", - "finecode.extension_runner.domain", -] [[tool.importlinter.contracts]] # such check doesn't control whether there are no raw requests in lsp_server, currently this should @@ -70,13 +63,16 @@ layers = [ id = "wm-use-runner-client" name = "WM uses LSP requests only in runner_client and lsp_server, no raw requests in app" type = "forbidden" -source_modules = ["finecode.workspace_manager"] +source_modules = ["finecode"] forbidden_modules = ["lsprotocol"] ignore_imports = [ - "finecode.workspace_manager.runner.runner_client -> lsprotocol", - "finecode.workspace_manager.lsp_server.lsp_server -> lsprotocol", + "finecode.runner.runner_client -> lsprotocol", + "finecode.lsp_server.lsp_server -> lsprotocol", ] [tool.setuptools_scm] version_file = "src/finecode/_version.py" + +[tool.setuptools.package-data] +finecode = ["base_config.toml"] diff --git a/src/finecode/base_config.toml b/src/finecode/base_config.toml index 6772ab2..520d83d 100644 --- a/src/finecode/base_config.toml +++ b/src/finecode/base_config.toml @@ -26,7 +26,7 @@ source = "finecode_extension_api.actions.prepare_envs.PrepareEnvsAction" name = "prepare_venvs" source = "fine_python_virtualenv.VirtualenvPrepareEnvHandler" env = "dev_workspace" -dependencies = ["fine_python_virtualenv==0.1.3"] +dependencies = ["fine_python_virtualenv==0.1.*"] [[tool.finecode.action.prepare_dev_workspaces_envs.handlers]] name = "prepare_envs_read_configs" @@ -47,7 +47,7 @@ source = "finecode_extension_api.actions.prepare_runners.PrepareRunnersAction" name = "prepare_runners_venvs" source = "fine_python_virtualenv.VirtualenvPrepareRunnersHandler" env = "dev_workspace" -dependencies = ["fine_python_virtualenv==0.1.3"] +dependencies = ["fine_python_virtualenv==0.1.*"] [[tool.finecode.action.prepare_runners.handlers]] name = "prepare_runners_read_configs" @@ -83,4 +83,4 @@ source = "finecode_extension_api.actions.install_deps_in_env.InstallDepsInEnvAct name = "install_deps_with_pip" source = "fine_python_pip.PipInstallDepsInEnvHandler" env = "dev_workspace" -dependencies = ["fine_python_pip==0.1.2"] +dependencies = ["fine_python_pip==0.1.*"] From 7b9eccff20d73ba2e9924a963bebd11925443bae Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 13:44:47 +0200 Subject: [PATCH 44/46] Remove build action --- .../finecode_extension_api/actions/build.py | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 finecode_extension_api/src/finecode_extension_api/actions/build.py diff --git a/finecode_extension_api/src/finecode_extension_api/actions/build.py b/finecode_extension_api/src/finecode_extension_api/actions/build.py deleted file mode 100644 index 1ae2fad..0000000 --- a/finecode_extension_api/src/finecode_extension_api/actions/build.py +++ /dev/null @@ -1,62 +0,0 @@ -import dataclasses -import pathlib -import sys -import typing - -if sys.version_info >= (3, 12): - from typing import override -else: - from typing_extensions import override - -from finecode_extension_api import code_action, textstyler - - -@dataclasses.dataclass -class BuildRunPayload(code_action.RunActionPayload): - # path to package root dir, where usually pyproject.toml is located. Note, that - # packages can have different layouts. Use service (TODO) to get package source - # directory path and avoid need to handle all possible cases by yourself. - package_root_path: pathlib.Path - build_type: typing.Literal["release"] | typing.Literal["debug"] = "release" - # TODO: entrypoint - # TODO: package type - # TODO: target platform? (including version etc) - - -class BuildRunContext(code_action.RunActionContext): - def __init__( - self, - run_id: int, - ) -> None: - super().__init__(run_id=run_id) - - -@dataclasses.dataclass -class BuildRunResult(code_action.RunActionResult): - # files/directories which are results of build - # TODO: for better abstraction we could split build and packaging even in case of - # wheel and sdist - results: list[pathlib.Path] - - @override - def update(self, other: code_action.RunActionResult) -> None: - if not isinstance(other, BuildRunResult): - return - - def to_text(self) -> str | textstyler.StyledText: - return "" - - -# general build action: any type of project should be built: library(pure and not pure python), application(both pure distributed as python package and application transformed to executable) -# concrete use cases: -# 1. Pure Python package built with `build` and `setuptools`, result: sdist and wheel. -# 2. Python Package with mypyc. TODO -# 3. Application distributed as python package: the same as use case 1. -# 4. Application compiled with Nuitka to executable. -# 5. Application packaged to executable with pyinstaller or similar tool. -# -# Customization examples: -# - Recognize constructs(syntax, imports) supported only by higher versions of python and replace them by alternatives from older python. One universal wheel will become version-specific wheel. -# - the same could be applied for platform-specific functionalities -# - optimize implementation -type BuildAction = code_action.Action[BuildRunPayload, BuildRunContext, BuildRunResult] From 28bc67d3a91d49543586ce172259f7529a2a3ec4 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 17:23:27 +0200 Subject: [PATCH 45/46] Fix name of command in github actions config --- .github/workflows/ci-cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index ea7d06a..8e47003 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -45,7 +45,7 @@ jobs: python -m pip install --upgrade pip==25.1.1 python -m pip install --group="dev_workspace" - python -m finecode prepare_envs + python -m finecode prepare-envs shell: bash # TODO: install all other supported python versions. Version can be extracted from finecode From b33358e2851aaa997544dd9411da67921671d2c7 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Thu, 28 Aug 2025 17:50:28 +0200 Subject: [PATCH 46/46] Update README --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bfb86e3..8e3a471 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # FineCode -**Status Update (04.06.2025):** FineCode is available for use, but the current version is intended only for personal testing, not for production use, as we actively work on improving execution environments, which will undergo significant changes. Initial release with new execution environments is expected in July 2025. - ## Personalize and improve your development experience FineCode is a tool runner and set of utilities for creating tools for software developers. @@ -45,10 +43,10 @@ NOTE: `pip install` supports `--group` parameter since pip 25.1. Make sure you h For list of presets from FineCode authors see 'Presets' section below. -1.2.1 Run `prepare_envs` finecode command: +1.2.1 Run `prepare-envs` finecode command: ```bash - python -m finecode prepare_envs + python -m finecode prepare-envs ``` 1.3 Enable finecode and preset