Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

# from finecode.extension_runner.interfaces import icache


def uri_str_to_path(uri_str: str) -> Path:
return Path(uri_str.replace("file://", ""))

Expand Down
2 changes: 1 addition & 1 deletion finecode_extension_runner/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ dependencies = [
"click==8.1.*",
"pydantic==2.11.*",
"platformdirs==4.3.*",
"pygls==2.0.0-a2",
"pygls==2.0.0-a6",
"finecode_extension_api==0.3.*",
"deepmerge==2.0.*",
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,10 @@ async def run_action(
f"R{run_id} | Run action end '{request.action_name}', duration: {duration}ms"
)

if not isinstance(action_result, code_action.RunActionResult):
# if partial results were sent, `action_result` may be None
if action_result is not None and not isinstance(
action_result, code_action.RunActionResult
):
logger.error(
f"R{run_id} | Unexpected result type: {type(action_result).__name__}"
)
Expand All @@ -284,7 +287,7 @@ async def run_action(


def action_result_to_run_action_response(
action_result: code_action.RunActionResult,
action_result: code_action.RunActionResult | None,
asked_result_format: typing.Literal["json"] | typing.Literal["string"],
) -> schemas.RunActionResponse:
serialized_result: dict[str, typing.Any] | str | None = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@ async def update_config(ls: lsp_server.LanguageServer, params):
raise e


def convert_path_keys(
obj: dict[str | pathlib.Path, typing.Any] | list[typing.Any],
) -> dict[str, typing.Any] | list[typing.Any]:
if isinstance(obj, dict):
return {str(k): convert_path_keys(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [convert_path_keys(item) for item in obj]
return obj


class CustomJSONEncoder(json.JSONEncoder):
# add support of serializing pathes to json.dumps
def default(self, obj):
Expand Down Expand Up @@ -240,7 +250,11 @@ async def run_action(ls: lsp_server.LanguageServer, params):

# 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"], cls=CustomJSONEncoder)
#
# custom json encoder converts dict values and `convert_path_keys` is used to
# convert dict keys
result_dict = convert_path_keys(response.to_dict()["result"])
result_str = json.dumps(result_dict, cls=CustomJSONEncoder)
return {
"status": status,
"result": result_str,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ class RunActionOptions(BaseSchema):
class RunActionResponse(BaseSchema):
return_code: int
# result can be empty(=None) e.g. if it was sent as a list of partial results
result: dict[str, Any] | None
result: dict[str, Any] | str | None
format: Literal["json"] | Literal["string"] | Literal["styled_text_json"] = "json"
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ dependencies = [
"watchdog==4.0.*",
"click==8.1.*",
"pydantic==2.11.*",
"platformdirs==4.3.*",
"pygls==2.0.0-a2",
"pygls==2.0.0-a6",
"finecode_extension_api==0.3.*",
"finecode_extension_runner==0.3.*",
"ordered-set==4.1.*",
Expand Down
8 changes: 0 additions & 8 deletions src/finecode/app_dirs.py

This file was deleted.

48 changes: 37 additions & 11 deletions src/finecode/cli_app/dump_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

from loguru import logger

from finecode import context, services
from finecode.config import config_models, read_configs
from finecode import context, proxy_utils, services
from finecode.config import collect_actions, config_models, read_configs
from finecode.runner import manager as runner_manager


Expand Down Expand Up @@ -40,23 +40,49 @@ async def dump_config(workdir_path: pathlib.Path, project_name: str):
f"Reading project configs(without presets) in {project.dir_path} failed: {exception.message}"
)

# 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"
project_def = ws_context.ws_projects[project_dir_path]
actions_by_projects = {project_dir_path: ["dump_config"]}

# start runner to init project config
try:
# reread projects configs, now with resolved presets
# to be able to resolve presets, start runners with presets first
try:
await runner_manager.update_runners(ws_context)
await runner_manager.start_runners_with_presets(
projects=[project_def], ws_context=ws_context
)
except runner_manager.RunnerFailedToStart as exception:
raise DumpFailed(
f"One or more projects are misconfigured, runners for them didn't"
f" start: {exception.message}. Check logs for details."
f"Starting runners with presets failed: {exception.message}"
)

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 DumpFailed(
f"Rereading project config with presets and collecting actions in {project.dir_path} failed: {exception.message}"
)

try:
await proxy_utils.start_required_environments(
actions_by_projects, ws_context
)
except proxy_utils.StartingEnvironmentsFailed as exception:
raise DumpFailed(
f"Failed to start environments for running 'dump_config': {exception.message}"
)

# 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"
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",
Expand Down
4 changes: 3 additions & 1 deletion src/finecode/config/read_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ async def read_projects_in_dir(
actions=actions,
env_configs={},
)
is_new_project = not def_file.parent in ws_context.ws_projects
ws_context.ws_projects[def_file.parent] = new_project
new_projects.append(new_project)
if is_new_project:
new_projects.append(new_project)
return new_projects


Expand Down
8 changes: 5 additions & 3 deletions src/finecode/logger_utils.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import inspect
import logging
import sys
from pathlib import Path

from loguru import logger

from finecode import app_dirs
from finecode_extension_runner import logs


def init_logger(trace: bool, stdout: bool = False):
log_dir_path = Path(app_dirs.get_app_dirs().user_log_dir)
venv_dir_path = Path(sys.executable) / ".." / ".."
logs_dir_path = venv_dir_path / "logs"

logger.remove()
# disable logging raw messages
# TODO: make configurable
Expand All @@ -20,7 +22,7 @@ def init_logger(trace: bool, stdout: bool = False):
]
)
logs.save_logs_to_file(
file_path=log_dir_path / "execution.log",
file_path=logs_dir_path / "workspace_manager.log",
log_level="TRACE" if trace else "INFO",
stdout=stdout,
)
Expand Down
9 changes: 6 additions & 3 deletions src/finecode/lsp_server/endpoints/action_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,12 @@ async def __reload_action(action_node_id: str) -> None:
)
for env in all_handlers_envs:
# parallel to speed up?
runner = global_state.ws_context.ws_projects_extension_runners[project_path][
env
]
try:
runner = global_state.ws_context.ws_projects_extension_runners[
project_path
][env]
except KeyError:
continue

try:
await runner_client.reload_action(runner, action_name)
Expand Down
31 changes: 25 additions & 6 deletions src/finecode/lsp_server/endpoints/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from loguru import logger
from lsprotocol import types
from pydantic.dataclasses import dataclass as pydantic_dataclass

from finecode import (
context,
Expand Down Expand Up @@ -78,9 +79,11 @@ async def document_diagnostic_with_full_result(
if response is None:
return None

lint_result: lint_action.LintRunResult = lint_action.LintRunResult(
**response.result
)
# use pydantic dataclass to convert dict to dataclass instance recursively
# (default dataclass constructor doesn't handle nested items, it stores them just
# as dict)
result_type = pydantic_dataclass(lint_action.LintRunResult)
lint_result: lint_action.LintRunResult = result_type(**response.result)

try:
requested_file_messages = lint_result.messages.pop(str(file_path))
Expand Down Expand Up @@ -142,8 +145,14 @@ async def document_diagnostic_with_partial_results(
related_documents: dict[str, types.FullDocumentDiagnosticReport] = {}
got_response_for_requested_file: bool = False
requested_file_path_str = str(file_path)
# use pydantic dataclass to convert dict to dataclass instance recursively
# (default dataclass constructor doesn't handle nested items, it stores them just
# as dict)
result_type = pydantic_dataclass(lint_action.LintRunResult)
async for partial_response in response:
lint_subresult = lint_action.LintRunResult(**partial_response)
lint_subresult: lint_action.LintRunResult = result_type(
**partial_response
)
for file_path_str, lint_messages in lint_subresult.messages.items():
if requested_file_path_str == file_path_str:
if got_response_for_requested_file:
Expand Down Expand Up @@ -244,8 +253,14 @@ async def run_workspace_diagnostic_with_partial_results(
project_dir_path=exec_info.project_dir_path,
ws_context=global_state.ws_context,
) as response:
# use pydantic dataclass to convert dict to dataclass instance recursively
# (default dataclass constructor doesn't handle nested items, it stores them just
# as dict)
result_type = pydantic_dataclass(lint_action.LintRunResult)
async for partial_response in response:
lint_subresult = lint_action.LintRunResult(**partial_response)
lint_subresult: lint_action.LintRunResult = result_type(
**partial_response
)
lsp_subresult = types.WorkspaceDiagnosticReportPartialResult(
items=[
types.WorkspaceFullDocumentDiagnosticReport(
Expand Down Expand Up @@ -310,12 +325,16 @@ async def workspace_diagnostic_with_full_result(

responses = [task.result().result for task in send_tasks]

# use pydantic dataclass to convert dict to dataclass instance recursively
# (default dataclass constructor doesn't handle nested items, it stores them just
# as dict)
result_type = pydantic_dataclass(lint_action.LintRunResult)
items: list[types.WorkspaceDocumentDiagnosticReport] = []
for response in responses:
if response is None:
continue
else:
lint_result = lint_action.LintRunResult(**response)
lint_result: lint_action.LintRunResult = result_type(**response)
for file_path_str, lint_messages in lint_result.messages.items():
new_report = types.WorkspaceFullDocumentDiagnosticReport(
uri=pygls_types_utils.path_to_uri_str(Path(file_path_str)),
Expand Down
Loading
Loading