From adbfbf5296c4ca04e152ae9986fcacdc7be06f80 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Fri, 28 Nov 2025 07:07:12 +0100 Subject: [PATCH 1/3] Enable Python 3.14 support --- extensions/fine_python_ast/pyproject.toml | 2 +- extensions/fine_python_black/pyproject.toml | 2 +- extensions/fine_python_flake8/pyproject.toml | 2 +- extensions/fine_python_import_linter/pyproject.toml | 2 +- extensions/fine_python_isort/pyproject.toml | 2 +- extensions/fine_python_module_exports/pyproject.toml | 2 +- extensions/fine_python_mypy/pyproject.toml | 2 +- extensions/fine_python_package_info/pyproject.toml | 2 +- extensions/fine_python_pip/pyproject.toml | 2 +- extensions/fine_python_pyrefly/pyproject.toml | 2 +- extensions/fine_python_ruff/pyproject.toml | 2 +- extensions/fine_python_virtualenv/pyproject.toml | 2 +- finecode_builtin_handlers/pyproject.toml | 2 +- finecode_dev_common_preset/pyproject.toml | 2 +- finecode_extension_api/pyproject.toml | 2 +- finecode_extension_runner/pyproject.toml | 2 +- presets/fine_python_format/pyproject.toml | 2 +- presets/fine_python_lint/pyproject.toml | 2 +- presets/fine_python_recommended/pyproject.toml | 2 +- tests/__testdata__/list_ws/backend/pyproject.toml | 2 +- tests/__testdata__/list_ws/cli_tool/pyproject.toml | 2 +- tests/__testdata__/list_ws/ui_app/pyproject.toml | 2 +- tests/__testdata__/nested_package/pyback/pyproject.toml | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/extensions/fine_python_ast/pyproject.toml b/extensions/fine_python_ast/pyproject.toml index 31ff2e7..de61a52 100644 --- a/extensions/fine_python_ast/pyproject.toml +++ b/extensions/fine_python_ast/pyproject.toml @@ -4,7 +4,7 @@ version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*"] [dependency-groups] diff --git a/extensions/fine_python_black/pyproject.toml b/extensions/fine_python_black/pyproject.toml index c0f808b..fca8767 100644 --- a/extensions/fine_python_black/pyproject.toml +++ b/extensions/fine_python_black/pyproject.toml @@ -4,7 +4,7 @@ version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*", "black (>=25.1.0,<26.0.0)"] [dependency-groups] diff --git a/extensions/fine_python_flake8/pyproject.toml b/extensions/fine_python_flake8/pyproject.toml index a8c9371..2939cf0 100644 --- a/extensions/fine_python_flake8/pyproject.toml +++ b/extensions/fine_python_flake8/pyproject.toml @@ -4,7 +4,7 @@ version = "0.2.1" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = [ "finecode_extension_api==0.3.*", "fine_python_ast==0.2.*", diff --git a/extensions/fine_python_import_linter/pyproject.toml b/extensions/fine_python_import_linter/pyproject.toml index bca107a..cbdec0c 100644 --- a/extensions/fine_python_import_linter/pyproject.toml +++ b/extensions/fine_python_import_linter/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">= 3.11, < 3.14" +requires-python = ">= 3.11, <= 3.14" dependencies = ["finecode_extension_api == 0.3.*", "import-linter (>=2.1,<3.0)"] diff --git a/extensions/fine_python_isort/pyproject.toml b/extensions/fine_python_isort/pyproject.toml index 6fe8bd9..8392d6f 100644 --- a/extensions/fine_python_isort/pyproject.toml +++ b/extensions/fine_python_isort/pyproject.toml @@ -4,7 +4,7 @@ version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">= 3.11, < 3.14" +requires-python = ">= 3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*", "isort (>=5.13, <6)"] [dependency-groups] diff --git a/extensions/fine_python_module_exports/pyproject.toml b/extensions/fine_python_module_exports/pyproject.toml index f41f6f9..e62bb0f 100644 --- a/extensions/fine_python_module_exports/pyproject.toml +++ b/extensions/fine_python_module_exports/pyproject.toml @@ -4,7 +4,7 @@ version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">= 3.11, < 3.14" +requires-python = ">= 3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*", "fine_python_ast==0.2.*"] [dependency-groups] diff --git a/extensions/fine_python_mypy/pyproject.toml b/extensions/fine_python_mypy/pyproject.toml index 5afecde..2f2e6ac 100644 --- a/extensions/fine_python_mypy/pyproject.toml +++ b/extensions/fine_python_mypy/pyproject.toml @@ -4,7 +4,7 @@ version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*", "mypy (>=1.15, <2.0)"] [dependency-groups] diff --git a/extensions/fine_python_package_info/pyproject.toml b/extensions/fine_python_package_info/pyproject.toml index bc18663..92aab9f 100644 --- a/extensions/fine_python_package_info/pyproject.toml +++ b/extensions/fine_python_package_info/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*", "tomlkit==0.11.*"] [dependency-groups] diff --git a/extensions/fine_python_pip/pyproject.toml b/extensions/fine_python_pip/pyproject.toml index 00749b3..db39e9a 100644 --- a/extensions/fine_python_pip/pyproject.toml +++ b/extensions/fine_python_pip/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.1" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*"] [dependency-groups] diff --git a/extensions/fine_python_pyrefly/pyproject.toml b/extensions/fine_python_pyrefly/pyproject.toml index 0fa9919..311a799 100644 --- a/extensions/fine_python_pyrefly/pyproject.toml +++ b/extensions/fine_python_pyrefly/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*", "pyrefly (>=0.30.0,<1.0.0)"] [dependency-groups] diff --git a/extensions/fine_python_ruff/pyproject.toml b/extensions/fine_python_ruff/pyproject.toml index 51525a4..8ab8116 100644 --- a/extensions/fine_python_ruff/pyproject.toml +++ b/extensions/fine_python_ruff/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*", "ruff (>=0.8.0,<1.0.0)"] [dependency-groups] diff --git a/extensions/fine_python_virtualenv/pyproject.toml b/extensions/fine_python_virtualenv/pyproject.toml index 248ff45..96e4781 100644 --- a/extensions/fine_python_virtualenv/pyproject.toml +++ b/extensions/fine_python_virtualenv/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = [ "finecode_extension_api==0.3.*", "virtualenv (>=20.0.0,<21.0.0)", diff --git a/finecode_builtin_handlers/pyproject.toml b/finecode_builtin_handlers/pyproject.toml index 65587fd..2edaed5 100644 --- a/finecode_builtin_handlers/pyproject.toml +++ b/finecode_builtin_handlers/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "FineCode built-in handlers" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*", "tomlkit==0.11.*"] [dependency-groups] diff --git a/finecode_dev_common_preset/pyproject.toml b/finecode_dev_common_preset/pyproject.toml index 38fa669..91782b9 100644 --- a/finecode_dev_common_preset/pyproject.toml +++ b/finecode_dev_common_preset/pyproject.toml @@ -4,7 +4,7 @@ version = "0.2.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = [ "fine_python_aksem @ git+https://github.com/Aksem/fine_python_aksem.git", "fine_python_recommended==0.4.*", diff --git a/finecode_extension_api/pyproject.toml b/finecode_extension_api/pyproject.toml index db9bf87..f3fd8e1 100644 --- a/finecode_extension_api/pyproject.toml +++ b/finecode_extension_api/pyproject.toml @@ -4,7 +4,7 @@ version = "0.3.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["typing-extensions (>=4.12.2,<5.0.0)"] [dependency-groups] diff --git a/finecode_extension_runner/pyproject.toml b/finecode_extension_runner/pyproject.toml index 8cc33cb..114e5e1 100644 --- a/finecode_extension_runner/pyproject.toml +++ b/finecode_extension_runner/pyproject.toml @@ -4,7 +4,7 @@ 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" +requires-python = ">=3.11, <= 3.14" dependencies = [ "loguru==0.7.*", "click==8.1.*", diff --git a/presets/fine_python_format/pyproject.toml b/presets/fine_python_format/pyproject.toml index 89eab10..31e6418 100644 --- a/presets/fine_python_format/pyproject.toml +++ b/presets/fine_python_format/pyproject.toml @@ -4,7 +4,7 @@ version = "0.3.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*"] [build-system] diff --git a/presets/fine_python_lint/pyproject.toml b/presets/fine_python_lint/pyproject.toml index 68f0de3..d92b471 100644 --- a/presets/fine_python_lint/pyproject.toml +++ b/presets/fine_python_lint/pyproject.toml @@ -4,7 +4,7 @@ version = "0.4.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["finecode_extension_api==0.3.*"] [build-system] diff --git a/presets/fine_python_recommended/pyproject.toml b/presets/fine_python_recommended/pyproject.toml index 48de1fc..3d3c846 100644 --- a/presets/fine_python_recommended/pyproject.toml +++ b/presets/fine_python_recommended/pyproject.toml @@ -4,7 +4,7 @@ version = "0.4.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = ["fine_python_format==0.3.*", "fine_python_lint==0.4.*"] [build-system] diff --git a/tests/__testdata__/list_ws/backend/pyproject.toml b/tests/__testdata__/list_ws/backend/pyproject.toml index d51aeb1..9a2de9e 100644 --- a/tests/__testdata__/list_ws/backend/pyproject.toml +++ b/tests/__testdata__/list_ws/backend/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" [tool.poetry.group.dev.dependencies] finecode = { path = "../../../", develop = true } diff --git a/tests/__testdata__/list_ws/cli_tool/pyproject.toml b/tests/__testdata__/list_ws/cli_tool/pyproject.toml index 6a227af..cc08b64 100644 --- a/tests/__testdata__/list_ws/cli_tool/pyproject.toml +++ b/tests/__testdata__/list_ws/cli_tool/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" dependencies = [] diff --git a/tests/__testdata__/list_ws/ui_app/pyproject.toml b/tests/__testdata__/list_ws/ui_app/pyproject.toml index 449292a..6f4461b 100644 --- a/tests/__testdata__/list_ws/ui_app/pyproject.toml +++ b/tests/__testdata__/list_ws/ui_app/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" [tool.poetry.group.dev.dependencies] finecode = { path = "../../../", develop = true } diff --git a/tests/__testdata__/nested_package/pyback/pyproject.toml b/tests/__testdata__/nested_package/pyback/pyproject.toml index 5756601..2d955b3 100644 --- a/tests/__testdata__/nested_package/pyback/pyproject.toml +++ b/tests/__testdata__/nested_package/pyback/pyproject.toml @@ -4,7 +4,7 @@ version = "0.1.0" description = "" authors = [{ name = "Vladyslav Hnatiuk", email = "aders1234@gmail.com" }] readme = "README.md" -requires-python = ">=3.11, < 3.14" +requires-python = ">=3.11, <= 3.14" [tool.poetry.group.dev.dependencies] finecode = { path = "../../../", develop = true } From 37660462b9abbf29e0174c3f60dc7753312db871 Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Fri, 28 Nov 2025 07:13:03 +0100 Subject: [PATCH 2/3] Format codebase --- .../fine_python_black/action.py | 4 +- .../fine_python_mypy/action.py | 2 +- .../fine_python_package_info/__init__.py | 5 +- .../list_project_files_by_lang_python.py | 47 ++++++---- .../py_package_layout_info_provider.py | 91 ++++++++++++++----- extensions/fine_python_package_info/setup.py | 2 +- .../tests/__init__.py | 2 +- .../install_deps_in_env_handler.py | 4 +- .../fine_python_pyrefly/__init__.py | 2 +- .../fine_python_pyrefly/lint_handler.py | 70 +++++++++----- extensions/fine_python_pyrefly/setup.py | 2 +- .../fine_python_pyrefly/tests/__init__.py | 2 +- .../fine_python_ruff/format_handler.py | 11 ++- .../fine_python_ruff/lint_handler.py | 23 +++-- tests/extension_runner/fixtures.py | 6 +- tests/workspace_manager/server/fixtures.py | 6 +- 16 files changed, 188 insertions(+), 91 deletions(-) diff --git a/extensions/fine_python_black/fine_python_black/action.py b/extensions/fine_python_black/fine_python_black/action.py index 7899e63..9f5d17f 100644 --- a/extensions/fine_python_black/fine_python_black/action.py +++ b/extensions/fine_python_black/fine_python_black/action.py @@ -131,7 +131,9 @@ def format_one(file_content: str, black_mode: Mode) -> tuple[str, bool]: # `fast` whether to validate code after formatting # `lines` is range to format new_file_content = black.format_file_contents( - file_content, fast=False, mode=black_mode # , lines=lines + file_content, + fast=False, + mode=black_mode, # , lines=lines ) file_changed = True except black.NothingChanged: diff --git a/extensions/fine_python_mypy/fine_python_mypy/action.py b/extensions/fine_python_mypy/fine_python_mypy/action.py index c52adf5..7723671 100644 --- a/extensions/fine_python_mypy/fine_python_mypy/action.py +++ b/extensions/fine_python_mypy/fine_python_mypy/action.py @@ -16,7 +16,7 @@ ifilemanager, ilogger, iextensionrunnerinfoprovider, - iprojectinfoprovider + iprojectinfoprovider, ) diff --git a/extensions/fine_python_package_info/fine_python_package_info/__init__.py b/extensions/fine_python_package_info/fine_python_package_info/__init__.py index 48da8ed..5c467d2 100644 --- a/extensions/fine_python_package_info/fine_python_package_info/__init__.py +++ b/extensions/fine_python_package_info/fine_python_package_info/__init__.py @@ -1,7 +1,4 @@ from .list_project_files_by_lang_python import ListProjectFilesByLangPythonHandler from .py_package_layout_info_provider import PyPackageLayoutInfoProvider -__all__ = [ - "ListProjectFilesByLangPythonHandler", - "PyPackageLayoutInfoProvider" -] +__all__ = ["ListProjectFilesByLangPythonHandler", "PyPackageLayoutInfoProvider"] diff --git a/extensions/fine_python_package_info/fine_python_package_info/list_project_files_by_lang_python.py b/extensions/fine_python_package_info/fine_python_package_info/list_project_files_by_lang_python.py index a279b86..5fccb74 100644 --- a/extensions/fine_python_package_info/fine_python_package_info/list_project_files_by_lang_python.py +++ b/extensions/fine_python_package_info/fine_python_package_info/list_project_files_by_lang_python.py @@ -1,9 +1,15 @@ -from finecode_extension_api.interfaces import iprojectinfoprovider, ipypackagelayoutinfoprovider, ilogger +from finecode_extension_api.interfaces import ( + iprojectinfoprovider, + ipypackagelayoutinfoprovider, + ilogger, +) import dataclasses import pathlib from finecode_extension_api import code_action -from finecode_extension_api.actions import list_project_files_by_lang as list_project_files_by_lang_action +from finecode_extension_api.actions import ( + list_project_files_by_lang as list_project_files_by_lang_action, +) @dataclasses.dataclass @@ -15,7 +21,8 @@ class ListProjectFilesByLangPythonHandlerConfig(code_action.ActionHandlerConfig) class ListProjectFilesByLangPythonHandler( code_action.ActionHandler[ - list_project_files_by_lang_action.ListProjectFilesByLangAction, ListProjectFilesByLangPythonHandlerConfig + list_project_files_by_lang_action.ListProjectFilesByLangAction, + ListProjectFilesByLangPythonHandlerConfig, ] ): def __init__( @@ -23,17 +30,19 @@ def __init__( config: ListProjectFilesByLangPythonHandlerConfig, project_info_provider: iprojectinfoprovider.IProjectInfoProvider, py_package_layout_info_provider: ipypackagelayoutinfoprovider.IPyPackageLayoutInfoProvider, - logger: ilogger.ILogger + logger: ilogger.ILogger, ) -> None: self.config = config self.project_info_provider = project_info_provider self.py_package_layout_info_provider = py_package_layout_info_provider self.logger = logger - self.current_project_dir_path = self.project_info_provider.get_current_project_dir_path() - self.tests_dir_path = self.current_project_dir_path / 'tests' - self.scripts_dir_path = self.current_project_dir_path / 'scripts' - self.setup_py_path = self.current_project_dir_path / 'setup.py' + self.current_project_dir_path = ( + self.project_info_provider.get_current_project_dir_path() + ) + self.tests_dir_path = self.current_project_dir_path / "tests" + self.scripts_dir_path = self.current_project_dir_path / "scripts" + self.setup_py_path = self.current_project_dir_path / "setup.py" async def run( self, @@ -41,26 +50,32 @@ async def run( run_context: list_project_files_by_lang_action.ListProjectFilesByLangRunContext, ) -> list_project_files_by_lang_action.ListProjectFilesByLangRunResult: py_files: list[pathlib.Path] = [] - project_package_src_root_dir_path = await self.py_package_layout_info_provider.get_package_src_root_dir_path(package_dir_path=self.current_project_dir_path) - py_files += list(project_package_src_root_dir_path.rglob('*.py')) + project_package_src_root_dir_path = ( + await self.py_package_layout_info_provider.get_package_src_root_dir_path( + package_dir_path=self.current_project_dir_path + ) + ) + py_files += list(project_package_src_root_dir_path.rglob("*.py")) if self.scripts_dir_path.exists(): - py_files += list(self.scripts_dir_path.rglob('*.py')) - + py_files += list(self.scripts_dir_path.rglob("*.py")) + if self.tests_dir_path.exists(): - py_files += list(self.tests_dir_path.rglob('*.py')) + py_files += list(self.tests_dir_path.rglob("*.py")) if self.setup_py_path.exists(): py_files.append(self.setup_py_path) - + if self.config.additional_dirs is not None: for dir_path in self.config.additional_dirs: dir_absolute_path = self.current_project_dir_path / dir_path if not dir_absolute_path.exists(): - self.logger.warning(f"Skip {dir_path} because {dir_absolute_path} doesn't exist") + self.logger.warning( + f"Skip {dir_path} because {dir_absolute_path} doesn't exist" + ) continue - py_files += list(dir_absolute_path.rglob('*.py')) + py_files += list(dir_absolute_path.rglob("*.py")) return list_project_files_by_lang_action.ListProjectFilesByLangRunResult( files_by_lang={"python": py_files} diff --git a/extensions/fine_python_package_info/fine_python_package_info/py_package_layout_info_provider.py b/extensions/fine_python_package_info/fine_python_package_info/py_package_layout_info_provider.py index fe749fa..58d52c0 100644 --- a/extensions/fine_python_package_info/fine_python_package_info/py_package_layout_info_provider.py +++ b/extensions/fine_python_package_info/fine_python_package_info/py_package_layout_info_provider.py @@ -3,7 +3,11 @@ import tomlkit import tomlkit.exceptions -from finecode_extension_api.interfaces import ifilemanager, ipypackagelayoutinfoprovider, icache +from finecode_extension_api.interfaces import ( + ifilemanager, + ipypackagelayoutinfoprovider, + icache, +) from finecode_extension_api import service @@ -12,68 +16,105 @@ def __init__(self, message: str) -> None: self.message = message -class PyPackageLayoutInfoProvider(ipypackagelayoutinfoprovider.IPyPackageLayoutInfoProvider, service.Service): - PACKAGE_NAME_CACHE_KEY = 'PyPackageLayoutInfoProviderPackageName' +class PyPackageLayoutInfoProvider( + ipypackagelayoutinfoprovider.IPyPackageLayoutInfoProvider, service.Service +): + PACKAGE_NAME_CACHE_KEY = "PyPackageLayoutInfoProviderPackageName" - def __init__(self, file_manager: ifilemanager.IFileManager, cache: icache.ICache) -> None: + def __init__( + self, file_manager: ifilemanager.IFileManager, cache: icache.ICache + ) -> None: self.file_manager = file_manager self.cache = cache async def _get_package_name(self, package_dir_path: pathlib.Path) -> str: # raises ConfigParseError - package_def_file = package_dir_path / 'pyproject.toml' + package_def_file = package_dir_path / "pyproject.toml" if not package_def_file.exists(): - raise NotImplementedError("Only python packages with pyproject.toml config file are supported") + raise NotImplementedError( + "Only python packages with pyproject.toml config file are supported" + ) try: - cached_package_name = await self.cache.get_file_cache(file_path=package_def_file, key=self.PACKAGE_NAME_CACHE_KEY) + cached_package_name = await self.cache.get_file_cache( + file_path=package_def_file, key=self.PACKAGE_NAME_CACHE_KEY + ) return cached_package_name except icache.CacheMissException: ... - package_def_file_content = await self.file_manager.get_content(file_path=package_def_file) - package_def_file_version = await self.file_manager.get_file_version(file_path=package_def_file) + package_def_file_content = await self.file_manager.get_content( + file_path=package_def_file + ) + package_def_file_version = await self.file_manager.get_file_version( + file_path=package_def_file + ) try: package_def_dict = tomlkit.loads(package_def_file_content) except tomlkit.exceptions.ParseError as exception: - raise ConfigParseError(f"Failed to parse package config {package_def_file}: toml parsing failed at {exception.line}:{exception.col}") from exception + raise ConfigParseError( + f"Failed to parse package config {package_def_file}: toml parsing failed at {exception.line}:{exception.col}" + ) from exception - package_raw_name = package_def_dict.get('project', {}).get('name', None) + package_raw_name = package_def_dict.get("project", {}).get("name", None) if package_raw_name is None: raise ValueError(f"package.name not found in {package_def_file}") - package_name = package_raw_name.replace('-', '_') - await self.cache.save_file_cache(file_path=package_def_file, file_version=package_def_file_version, key=self.PACKAGE_NAME_CACHE_KEY, value=package_name) + package_name = package_raw_name.replace("-", "_") + await self.cache.save_file_cache( + file_path=package_def_file, + file_version=package_def_file_version, + key=self.PACKAGE_NAME_CACHE_KEY, + value=package_name, + ) return package_name - async def get_package_layout(self, package_dir_path: pathlib.Path) -> ipypackagelayoutinfoprovider.PyPackageLayout: - if (package_dir_path / 'src').exists(): + async def get_package_layout( + self, package_dir_path: pathlib.Path + ) -> ipypackagelayoutinfoprovider.PyPackageLayout: + if (package_dir_path / "src").exists(): return ipypackagelayoutinfoprovider.PyPackageLayout.SRC else: try: - package_name = await self._get_package_name(package_dir_path=package_dir_path) + package_name = await self._get_package_name( + package_dir_path=package_dir_path + ) except ConfigParseError as exception: - raise ipypackagelayoutinfoprovider.FailedToGetPackageLayout(exception.message) - + raise ipypackagelayoutinfoprovider.FailedToGetPackageLayout( + exception.message + ) + if (package_dir_path / package_name).exists(): return ipypackagelayoutinfoprovider.PyPackageLayout.FLAT else: return ipypackagelayoutinfoprovider.PyPackageLayout.CUSTOM - async def get_package_src_root_dir_path(self, package_dir_path: str) -> pathlib.Path: + async def get_package_src_root_dir_path( + self, package_dir_path: str + ) -> pathlib.Path: try: - package_layout = await self.get_package_layout(package_dir_path=package_dir_path) + package_layout = await self.get_package_layout( + package_dir_path=package_dir_path + ) except ipypackagelayoutinfoprovider.FailedToGetPackageLayout as exception: - raise ipypackagelayoutinfoprovider.FailedToGetPackageSrcRootDirPath(exception.message) + raise ipypackagelayoutinfoprovider.FailedToGetPackageSrcRootDirPath( + exception.message + ) try: - package_name = await self._get_package_name(package_dir_path=package_dir_path) + package_name = await self._get_package_name( + package_dir_path=package_dir_path + ) except ConfigParseError as exception: - raise ipypackagelayoutinfoprovider.FailedToGetPackageSrcRootDirPath(exception.message) + raise ipypackagelayoutinfoprovider.FailedToGetPackageSrcRootDirPath( + exception.message + ) if package_layout == ipypackagelayoutinfoprovider.PyPackageLayout.SRC: - return package_dir_path / 'src' / package_name + return package_dir_path / "src" / package_name elif package_layout == ipypackagelayoutinfoprovider.PyPackageLayout.FLAT: return package_dir_path / package_name else: - raise NotImplementedError(f"Custom python package layout in {package_dir_path} is not supported") + raise NotImplementedError( + f"Custom python package layout in {package_dir_path} is not supported" + ) diff --git a/extensions/fine_python_package_info/setup.py b/extensions/fine_python_package_info/setup.py index f573cbe..7715245 100644 --- a/extensions/fine_python_package_info/setup.py +++ b/extensions/fine_python_package_info/setup.py @@ -64,4 +64,4 @@ def initialize_options(self): "build_ext": CustomBuildExt, "egg_info": CustomEggInfo, }, -) \ No newline at end of file +) diff --git a/extensions/fine_python_package_info/tests/__init__.py b/extensions/fine_python_package_info/tests/__init__.py index ddbc937..98726bb 100644 --- a/extensions/fine_python_package_info/tests/__init__.py +++ b/extensions/fine_python_package_info/tests/__init__.py @@ -1 +1 @@ -# Tests for fine_python_package_info extension \ No newline at end of file +# Tests for fine_python_package_info extension 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 40d5bf4..bec5840 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 @@ -66,7 +66,9 @@ def _construct_pip_install_cmd( install_params += f'--find-links="{link}" ' if self.config.editable_mode is not None: - install_params += f"--config-settings editable_mode='{self.config.editable_mode}' " + install_params += ( + f"--config-settings editable_mode='{self.config.editable_mode}' " + ) for dependency in dependencies: if dependency.editable: diff --git a/extensions/fine_python_pyrefly/fine_python_pyrefly/__init__.py b/extensions/fine_python_pyrefly/fine_python_pyrefly/__init__.py index d9e6a9c..2b0ee58 100644 --- a/extensions/fine_python_pyrefly/fine_python_pyrefly/__init__.py +++ b/extensions/fine_python_pyrefly/fine_python_pyrefly/__init__.py @@ -3,4 +3,4 @@ __all__ = [ "PyreflyLintHandler", "PyreflyLintHandlerConfig", -] \ No newline at end of file +] diff --git a/extensions/fine_python_pyrefly/fine_python_pyrefly/lint_handler.py b/extensions/fine_python_pyrefly/fine_python_pyrefly/lint_handler.py index 08893fc..e996f7b 100644 --- a/extensions/fine_python_pyrefly/fine_python_pyrefly/lint_handler.py +++ b/extensions/fine_python_pyrefly/fine_python_pyrefly/lint_handler.py @@ -7,7 +7,14 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import lint as lint_action -from finecode_extension_api.interfaces import icache, icommandrunner, ilogger, ifilemanager, iprojectfileclassifier, iextensionrunnerinfoprovider +from finecode_extension_api.interfaces import ( + icache, + icommandrunner, + ilogger, + ifilemanager, + iprojectfileclassifier, + iextensionrunnerinfoprovider, +) @dataclasses.dataclass @@ -23,6 +30,7 @@ class PyreflyLintHandler( FineCode. In environments like IDE, messages from pyrefly will be updated only after save of a file. """ + CACHE_KEY = "PyreflyLinter" def __init__( @@ -33,7 +41,7 @@ def __init__( file_manager: ifilemanager.IFileManager, command_runner: icommandrunner.ICommandRunner, project_file_classifier: iprojectfileclassifier.IProjectFileClassifier, - extension_runner_info_provider: iextensionrunnerinfoprovider.IExtensionRunnerInfoProvider + extension_runner_info_provider: iextensionrunnerinfoprovider.IExtensionRunnerInfoProvider, ) -> None: self.config = config self.cache = cache @@ -42,12 +50,10 @@ def __init__( self.command_runner = command_runner self.project_file_classifier = project_file_classifier self.extension_runner_info_provider = extension_runner_info_provider - + self.pyrefly_bin_path = Path(sys.executable).parent / "pyrefly" - async def run_on_single_file( - self, file_path: Path - ) -> lint_action.LintRunResult: + async def run_on_single_file(self, file_path: Path) -> lint_action.LintRunResult: messages = {} try: cached_lint_messages = await self.cache.get_file_cache( @@ -86,18 +92,34 @@ async def run_pyrefly_lint_on_single_file( ) -> list[lint_action.LintMessage]: """Run pyrefly type checking on a single file""" lint_messages: list[lint_action.LintMessage] = [] - + try: # project file classifier caches result, we can just get it each time again - file_type = self.project_file_classifier.get_project_file_type(file_path=file_path) - file_env = self.project_file_classifier.get_env_for_file_type(file_type=file_type) + file_type = self.project_file_classifier.get_project_file_type( + file_path=file_path + ) + file_env = self.project_file_classifier.get_env_for_file_type( + file_type=file_type + ) except NotImplementedError: - self.logger.warning(f"Skip {file_path} because file type or env for it could be determined") + self.logger.warning( + f"Skip {file_path} because file type or env for it could be determined" + ) return lint_messages - venv_dir_path = self.extension_runner_info_provider.get_venv_dir_path_of_env(env_name=file_env) - site_package_pathes = self.extension_runner_info_provider.get_venv_site_packages(venv_dir_path=venv_dir_path) - interpreter_path = self.extension_runner_info_provider.get_venv_python_interpreter(venv_dir_path=venv_dir_path) + venv_dir_path = self.extension_runner_info_provider.get_venv_dir_path_of_env( + env_name=file_env + ) + site_package_pathes = ( + self.extension_runner_info_provider.get_venv_site_packages( + venv_dir_path=venv_dir_path + ) + ) + interpreter_path = ( + self.extension_runner_info_provider.get_venv_python_interpreter( + venv_dir_path=venv_dir_path + ) + ) # --skip-interpreter-query isn't used because it is not compatible # with --python-interpreter parameter @@ -110,29 +132,31 @@ async def run_pyrefly_lint_on_single_file( "--output-format=json", # path to python interpreter because pyrefly resolves .pth files only if # it is provided - f"--python-interpreter='{str(interpreter_path)}'" + f"--python-interpreter='{str(interpreter_path)}'", ] if self.config.python_version is not None: cmd.append(f"--python-version='{self.config.python_version}'") for path in site_package_pathes: - cmd.append(f'--site-package-path={str(path)}') + cmd.append(f"--site-package-path={str(path)}") cmd.append(str(file_path)) cmd_str = " ".join(cmd) pyrefly_process = await self.command_runner.run(cmd_str) await pyrefly_process.wait_for_end() - + output = pyrefly_process.get_output() try: pyrefly_results = json.loads(output) - for error in pyrefly_results['errors']: + for error in pyrefly_results["errors"]: lint_message = map_pyrefly_error_to_lint_message(error) lint_messages.append(lint_message) except json.JSONDecodeError: - raise code_action.ActionFailedException(f'Output of pyrefly is not json: {output}') + raise code_action.ActionFailedException( + f"Output of pyrefly is not json: {output}" + ) return lint_messages @@ -140,13 +164,13 @@ async def run_pyrefly_lint_on_single_file( def map_pyrefly_error_to_lint_message(error: dict) -> lint_action.LintMessage: """Map a pyrefly error to a lint message""" # Extract line/column info (pyrefly uses 1-based indexing) - start_line = error['line'] - start_column = error['column'] - end_line = error['stop_line'] - end_column = error['stop_column'] + start_line = error["line"] + start_column = error["column"] + end_line = error["stop_line"] + end_column = error["stop_column"] # Determine severity based on error type - error_code = str(error.get('code', '')) + error_code = str(error.get("code", "")) code_description = error.get("name", "") severity = lint_action.LintMessageSeverity.ERROR diff --git a/extensions/fine_python_pyrefly/setup.py b/extensions/fine_python_pyrefly/setup.py index 7809fb7..4c12629 100644 --- a/extensions/fine_python_pyrefly/setup.py +++ b/extensions/fine_python_pyrefly/setup.py @@ -64,4 +64,4 @@ def initialize_options(self): "build_ext": CustomBuildExt, "egg_info": CustomEggInfo, }, -) \ No newline at end of file +) diff --git a/extensions/fine_python_pyrefly/tests/__init__.py b/extensions/fine_python_pyrefly/tests/__init__.py index 39a7300..115302f 100644 --- a/extensions/fine_python_pyrefly/tests/__init__.py +++ b/extensions/fine_python_pyrefly/tests/__init__.py @@ -1 +1 @@ -# Tests for fine_python_pyrefly extension \ No newline at end of file +# Tests for fine_python_pyrefly extension diff --git a/extensions/fine_python_ruff/fine_python_ruff/format_handler.py b/extensions/fine_python_ruff/fine_python_ruff/format_handler.py index 44d5ed6..ca3f0e8 100644 --- a/extensions/fine_python_ruff/fine_python_ruff/format_handler.py +++ b/extensions/fine_python_ruff/fine_python_ruff/format_handler.py @@ -13,7 +13,12 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import format as format_action -from finecode_extension_api.interfaces import icache, icommandrunner, ilogger, iextensionrunnerinfoprovider +from finecode_extension_api.interfaces import ( + icache, + icommandrunner, + ilogger, + iextensionrunnerinfoprovider, +) @dataclasses.dataclass @@ -91,7 +96,9 @@ async def format_one(self, file_path: Path, file_content: str) -> tuple[str, boo str(self.ruff_bin_path), "format", "--cache-dir", - str(self.extension_runner_info_provider.get_cache_dir_path() / ".ruff_cache"), + str( + self.extension_runner_info_provider.get_cache_dir_path() / ".ruff_cache" + ), "--line-length", str(self.config.line_length), f'--config="indent-width={str(self.config.indent_width)}"', diff --git a/extensions/fine_python_ruff/fine_python_ruff/lint_handler.py b/extensions/fine_python_ruff/fine_python_ruff/lint_handler.py index 66e726c..41c1706 100644 --- a/extensions/fine_python_ruff/fine_python_ruff/lint_handler.py +++ b/extensions/fine_python_ruff/fine_python_ruff/lint_handler.py @@ -7,7 +7,12 @@ from finecode_extension_api import code_action from finecode_extension_api.actions import lint as lint_action -from finecode_extension_api.interfaces import icache, icommandrunner, ilogger, ifilemanager +from finecode_extension_api.interfaces import ( + icache, + icommandrunner, + ilogger, + ifilemanager, +) @dataclasses.dataclass @@ -38,12 +43,10 @@ def __init__( self.logger = logger self.file_manager = file_manager self.command_runner = command_runner - + self.ruff_bin_path = Path(sys.executable).parent / "ruff" - async def run_on_single_file( - self, file_path: Path - ) -> lint_action.LintRunResult: + async def run_on_single_file(self, file_path: Path) -> lint_action.LintRunResult: messages = {} try: cached_lint_messages = await self.cache.get_file_cache( @@ -112,12 +115,12 @@ async def run_ruff_lint_on_single_file( ruff_process = await self.command_runner.run( cmd_str, ) - + ruff_process.write_to_stdin(file_content) ruff_process.close_stdin() # Signal EOF - + await ruff_process.wait_for_end() - + output = ruff_process.get_output() try: ruff_results = json.loads(output) @@ -125,7 +128,9 @@ async def run_ruff_lint_on_single_file( lint_message = map_ruff_violation_to_lint_message(violation) lint_messages.append(lint_message) except json.JSONDecodeError: - raise code_action.ActionFailedException(f'Output of ruff is not json: {output}') + raise code_action.ActionFailedException( + f"Output of ruff is not json: {output}" + ) return lint_messages diff --git a/tests/extension_runner/fixtures.py b/tests/extension_runner/fixtures.py index eaffcd8..0f32533 100644 --- a/tests/extension_runner/fixtures.py +++ b/tests/extension_runner/fixtures.py @@ -39,8 +39,10 @@ async def runner_client_channel(): for transport in app.transports if isinstance(transport, InMemoryTransport) ) - except StopIteration: - raise Exception("App configuration error. InMemory transport not found") + except StopIteration as exception: + raise Exception( + "App configuration error. InMemory transport not found" + ) from exception channel = InMemoryChannel(transport=inmemory_transport, converter=json_converter) client = Client(channel=channel) diff --git a/tests/workspace_manager/server/fixtures.py b/tests/workspace_manager/server/fixtures.py index 60c8ce0..1a206dc 100644 --- a/tests/workspace_manager/server/fixtures.py +++ b/tests/workspace_manager/server/fixtures.py @@ -39,8 +39,10 @@ async def client_channel(): for transport in app.transports if isinstance(transport, InMemoryTransport) ) - except StopIteration: - raise Exception("App configuration error. InMemory transport not found") + except StopIteration as exception: + raise Exception( + "App configuration error. InMemory transport not found" + ) from exception channel = InMemoryChannel(transport=inmemory_transport, converter=json_converter) client = Client(channel=channel) From d1a4d794bed163fa63b296ed6e601641e20ed8af Mon Sep 17 00:00:00 2001 From: Vladyslav Hnatiuk Date: Fri, 28 Nov 2025 07:13:38 +0100 Subject: [PATCH 3/3] Use local packages in dev common preset --- .../src/finecode_dev_common_preset/preset.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) 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 0914817..1799cf3 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 @@ -9,15 +9,31 @@ presets = [ # finecode_extension_runner must match [tool.finecode.env.runtime.dependencies] finecode_extension_runner = { path = "../../../finecode_extension_runner", editable = true } +finecode_extension_api = { path = "../../../finecode_extension_api", editable = true } [tool.finecode.env.dev.dependencies] finecode_extension_runner = { path = "../../../finecode_extension_runner", editable = true } [tool.finecode.env.dev_no_runtime.dependencies] +finecode_extension_api = { path = "../../../finecode_extension_api", editable = true } finecode_extension_runner = { path = "../../../finecode_extension_runner", editable = true } +fine_python_mypy = { path = "../../../extensions/fine_python_mypy", editable = true } +fine_python_ruff = { path = "../../../extensions/fine_python_ruff", editable = true } +fine_python_flake8 = { path = "../../../extensions/fine_python_flake8", editable = true } +fine_python_pyrefly = { path = "../../../extensions/fine_python_pyrefly", editable = true } +fine_python_package_info = { path = "../../../extensions/fine_python_package_info", editable = 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" + +# TODO: recognize minimal python version automatically +[[tool.finecode.action_handler]] +source = "fine_python_ruff.RuffLintHandler" +config.target_version = 'py311' + +[[tool.finecode.action_handler]] +source = "fine_python_pyrefly.PyreflyLintHandler" +config.python_version = '3.11'