Skip to content

Conversation

mohammedahmed18
Copy link
Contributor

@mohammedahmed18 mohammedahmed18 commented Sep 20, 2025

PR Type

Enhancement


Description

  • Move pyproject discovery to init feature

  • Add project init/validation endpoint

  • Simplify server initialization flow

  • Minor logging and variable cleanups


Diagram Walkthrough

flowchart LR
  Server["Language Server startup"] -- "no pyproject lookup" --> InitFeature["initProject feature"]
  InitFeature -- "optional config path" --> PrepareArgs["prepare_optimizer_arguments()"]
  InitFeature -- "search workspace (depth≤2)" --> FindPyproject["_find_pyproject_toml()"]
  InitFeature -- "validate file" --> Validate["is_valid_pyproject_toml()"]
  Validate -- "success" --> RepoChecks["Git repo checks"]
  RepoChecks -- "success" --> Ready["Return moduleRoot + pyprojectPath"]
  Validate -- "failure" --> Error["Return validation error"]
Loading

File Walkthrough

Relevant files
Enhancement
beta.py
Add initProject with pyproject discovery and validation   

codeflash/lsp/beta.py

  • Remove protocol_cls usage in server creation.
  • Introduce ValidateProjectParams dataclass.
  • Add initProject feature with pyproject discovery and validation.
  • Implement bounded-depth search for tool.codeflash in pyproject.
  • Return pyprojectPath in responses; support skip_validation.
  • Minor variable rename for discovered tests count.
+59/-8   
server.py
Remove pyproject lookup from LSP initialize                           

codeflash/lsp/server.py

  • Remove initialize-time pyproject discovery logic.
  • Drop custom lsp_initialize override and helpers.
  • Simplify imports and protocol usage.
+3/-29   
Formatting
lsp_logger.py
Minor logging comment cleanup                                                       

codeflash/lsp/lsp_logger.py

  • Tweak comment to generalize stderr handler purpose.
+1/-1     

Copy link

github-actions bot commented Sep 20, 2025

PR Reviewer Guide 🔍

(Review updated until commit da2b676)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible None usage

In the new initProject flow, the code logs "Found pyproject.toml at: None" and may proceed without ensuring pyproject_toml_path is non-None in all branches (e.g., skip_validation True). Ensure pyproject_toml_path is set after prepare_optimizer_arguments or guard log/return paths to avoid None being returned or logged.

# should be called the first thing to initialize and validate the project
@server.feature("initProject")
def init_project(server: CodeflashLanguageServer, params: ValidateProjectParams) -> dict[str, str]:
    from codeflash.cli_cmds.cmd_init import is_valid_pyproject_toml

    pyproject_toml_path: Path | None = getattr(params, "config_file", None)

    if server.args is None:
        if pyproject_toml_path is not None:
            # if there is a config file provided use it
            server.prepare_optimizer_arguments(pyproject_toml_path)
        else:
            # otherwise look for it
            pyproject_toml_path = _find_pyproject_toml(params.root_path_abs)
            server.show_message_log(f"Found pyproject.toml at: {pyproject_toml_path}", "Info")
            if pyproject_toml_path:
                server.prepare_optimizer_arguments(pyproject_toml_path)
            else:
                return {
                    "status": "error",
                    "message": "No pyproject.toml found in workspace.",
                }  # TODO: enhancec this message to say there is not tool.codeflash in pyproject.toml or smth

    if getattr(params, "skip_validation", False):
        return {"status": "success", "moduleRoot": server.args.module_root, "pyprojectPath": pyproject_toml_path}
Validation bypass risks

When skip_validation is True, the handler returns success using server.args.module_root and pyprojectPath without confirming args were prepared or pyproject_toml_path is valid. Consider verifying server.args is initialized and pyproject path exists before returning success.

if getattr(params, "skip_validation", False):
    return {"status": "success", "moduleRoot": server.args.module_root, "pyprojectPath": pyproject_toml_path}
Resource handling

devnull_writer is opened but never closed explicitly. Wrap in a with-statement to avoid leaking a file descriptor.

devnull_writer = open(os.devnull, "w")  # noqa
with contextlib.redirect_stdout(devnull_writer):
    function_to_tests, _num_discovered_tests = server.optimizer.discover_tests(optimizable_funcs)
    function_optimizer.function_to_tests = function_to_tests

Copy link

github-actions bot commented Sep 20, 2025

PR Code Suggestions ✨

Latest suggestions up to da2b676
Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Security
Harden and bound directory traversal

Restrict directory traversal to the workspace and avoid following symlinks to
prevent escaping and performance hits. Also short-circuit reading the file once the
target section is found and handle potential I/O errors gracefully.

codeflash/lsp/beta.py [171-189]

 def _find_pyproject_toml(workspace_path: str) -> Path | None:
-    workspace_path_obj = Path(workspace_path)
+    workspace_path_obj = Path(workspace_path).resolve()
     max_depth = 2
     base_depth = len(workspace_path_obj.parts)
 
-    for root, dirs, files in os.walk(workspace_path_obj):
-        depth = len(Path(root).parts) - base_depth
+    for root, dirs, files in os.walk(workspace_path_obj, topdown=True, followlinks=False):
+        # ensure we don't traverse outside workspace due to symlinks
+        try:
+            root_path = Path(root).resolve()
+        except Exception:
+            continue
+        if not str(root_path).startswith(str(workspace_path_obj)):
+            dirs.clear()
+            continue
+
+        depth = len(root_path.parts) - base_depth
         if depth > max_depth:
-            # stop going deeper into this branch
             dirs.clear()
             continue
 
         if "pyproject.toml" in files:
-            file_path = Path(root) / "pyproject.toml"
-            with file_path.open("r", encoding="utf-8", errors="ignore") as f:
-                for line in f:
-                    if line.strip() == "[tool.codeflash]":
-                        return file_path.resolve()
+            file_path = root_path / "pyproject.toml"
+            try:
+                with file_path.open("r", encoding="utf-8", errors="ignore") as f:
+                    for line in f:
+                        if line.strip() == "[tool.codeflash]":
+                            return file_path
+            except (OSError, IOError):
+                continue
     return None
Suggestion importance[1-10]: 7

__

Why: The proposal improves safety and robustness by preventing symlink escapes, bounding traversal, and handling I/O errors, aligning with the new helper’s intent; it’s accurate and beneficial though not critical.

Medium
Possible issue
Add null checks before dereference

Guard against None pyproject_toml_path and missing server.args before dereferencing
to avoid AttributeError. Return a clear error when neither a config is provided nor
discovered.

codeflash/lsp/beta.py [193-225]

 @server.feature("initProject")
 def init_project(server: CodeflashLanguageServer, params: ValidateProjectParams) -> dict[str, str]:
     from codeflash.cli_cmds.cmd_init import is_valid_pyproject_toml
 
     pyproject_toml_path: Path | None = getattr(params, "config_file", None)
 
     if server.args is None:
         if pyproject_toml_path is not None:
-            # if there is a config file provided use it
             server.prepare_optimizer_arguments(pyproject_toml_path)
         else:
-            # otherwise look for it
             pyproject_toml_path = _find_pyproject_toml(params.root_path_abs)
             server.show_message_log(f"Found pyproject.toml at: {pyproject_toml_path}", "Info")
             if pyproject_toml_path:
                 server.prepare_optimizer_arguments(pyproject_toml_path)
             else:
                 return {
                     "status": "error",
                     "message": "No pyproject.toml found in workspace.",
-                }  # TODO: enhancec this message to say there is not tool.codeflash in pyproject.toml or smth
+                }
 
     if getattr(params, "skip_validation", False):
+        if server.args is None or pyproject_toml_path is None:
+            return {
+                "status": "error",
+                "message": "Project not initialized; missing pyproject.toml.",
+            }
         return {"status": "success", "moduleRoot": server.args.module_root, "pyprojectPath": pyproject_toml_path}
 
     server.show_message_log("Validating project...", "Info")
+    if pyproject_toml_path is None:
+        return {
+            "status": "error",
+            "message": "No pyproject.toml path provided or discovered.",
+        }
     config = is_valid_pyproject_toml(pyproject_toml_path)
     if config is None:
         server.show_message_log("pyproject.toml is not valid", "Error")
         return {
             "status": "error",
-            "message": "pyproject.toml is not valid",  # keep the error message the same, the extension is matching "pyproject.toml" in the error message to show the codeflash init instructions,
+            "message": "pyproject.toml is not valid",
         }
+    # continue with repo validation...
Suggestion importance[1-10]: 6

__

Why: Adding guards for server.args and pyproject_toml_path prevents potential AttributeError/None dereferences and clarifies error paths; it’s a sensible defensive improvement with moderate impact.

Low
General
Restore custom protocol usage

Preserve the explicit protocol class to ensure custom initialization hooks and
protocol behavior remain effective. Removing it may bypass expected lifecycle
handling.

codeflash/lsp/beta.py [71]

-server = CodeflashLanguageServer("codeflash-language-server", "v1.0")
+server = CodeflashLanguageServer("codeflash-language-server", "v1.0", protocol_cls=CodeflashLanguageServerProtocol)
Suggestion importance[1-10]: 5

__

Why: Reintroducing protocol_cls=CodeflashLanguageServerProtocol could be important if custom protocol behavior is relied upon, but the PR appears to deliberately remove it; without clear regression, impact is uncertain.

Low

Previous suggestions

Suggestions up to commit 90cdf3e
CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent attribute access error

Guard against server.args being unset when skipping validation; returning
server.args.module_root may raise if arguments were not prepared yet. Return None or
params.root_path_abs for module root in this path when server.args is absent.

codeflash/lsp/beta.py [214-216]

 if getattr(params, "skip_validation", False):
-    return {"status": "success", "moduleRoot": server.args.module_root, "pyprojectPath": pyproject_toml_path}
+    module_root = getattr(getattr(server, "args", None), "module_root", None) or params.root_path_abs
+    return {"status": "success", "moduleRoot": module_root, "pyprojectPath": pyproject_toml_path}
Suggestion importance[1-10]: 7

__

Why: Accessing server.args.module_root can raise if args weren't prepared; the guard uses params.root_path_abs fallback, which is sensible and aligns with the new param schema.

Medium
Validate path before use

Ensure a valid path is passed to validation; pyproject_toml_path can be None when
provided config_file is invalid or missing. Add an explicit check and return a clear
error before calling validation to avoid downstream exceptions.

codeflash/lsp/beta.py [217-219]

 server.show_message_log("Validating project...", "Info")
+if not pyproject_toml_path:
+    return {
+        "status": "error",
+        "message": "pyproject.toml path is not set or not found in workspace.",
+    }
 config = is_valid_pyproject_toml(pyproject_toml_path)
Suggestion importance[1-10]: 5

__

Why: Adding a pre-check for a None pyproject_toml_path is reasonable, though earlier logic should have set it; this is a defensive, low-to-moderate impact correctness improvement.

Low
General
Fix misleading logging

Avoid logging a found path when pyproject_toml_path is None, which can produce
misleading "Found ... at: None" messages. Log only after confirming a valid path,
and include the workspace path in the error to aid debugging.

codeflash/lsp/beta.py [205-213]

-server.show_message_log(f"Found pyproject.toml at: {pyproject_toml_path}", "Info")
 if pyproject_toml_path:
+    server.show_message_log(f"Found pyproject.toml at: {pyproject_toml_path}", "Info")
     server.prepare_optimizer_arguments(pyproject_toml_path)
 else:
     return {
         "status": "error",
-        "message": "No pyproject.toml found in workspace.",
+        "message": f"No pyproject.toml found in workspace: {params.root_path_abs}",
     }
Suggestion importance[1-10]: 6

__

Why: Correctly moves the info log inside the truthy branch to avoid logging a misleading "Found ... None" and enhances the error message; a minor but accurate improvement grounded in the diff.

Low

@mohammedahmed18 mohammedahmed18 marked this pull request as draft September 20, 2025 15:38
@mohammedahmed18 mohammedahmed18 marked this pull request as ready for review September 22, 2025 20:56
Copy link

Persistent review updated to latest commit da2b676

Saga4
Saga4 previously approved these changes Sep 22, 2025
@Saga4 Saga4 merged commit e84e57a into main Sep 22, 2025
17 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants