Skip to content

Remove unused verbose parameter#11

Merged
leynos merged 5 commits intomainfrom
codex/remove-unused-verbose-parameter
Aug 7, 2025
Merged

Remove unused verbose parameter#11
leynos merged 5 commits intomainfrom
codex/remove-unused-verbose-parameter

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Aug 7, 2025

Summary

  • drop unused verbose arg from render_block
  • fix type annotations by importing asyncio.subprocess
  • rename test to reflect logging behavior

Testing

  • make fmt
  • make lint
  • make typecheck
  • make test

https://chatgpt.com/codex/tasks/task_e_6894ef14e6a083229493ae05c0a8684e

Summary by Sourcery

Clean up render_block by dropping the unused verbose argument, updating subprocess type imports, and renaming the test to match logging behavior

Enhancements:

  • Remove the unused verbose parameter from render_block
  • Fix type annotations by aliasing asyncio.subprocess imports
  • Simplify render_block docstring to reflect INFO-level logging

Tests:

  • Rename test_render_block to reflect warning-level logging behavior

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Aug 7, 2025

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Removed the unused verbose parameter from render_block, updated subprocess type annotations to use the imported alias, and renamed the test to reflect the revised logging behavior.

Class diagram for updated render_block and wait_for_proc signatures

classDiagram
    class render_block {
        +async render_block(path: Path, idx: int, semaphore: asyncio.Semaphore, timeout: float = 30.0) bool
    }
    class wait_for_proc {
        +async wait_for_proc(proc: asyncio_subprocess.Process, path: Path, idx: int, timeout: float = 30.0) tuple[bool, bytes]
    }
    render_block --> wait_for_proc : calls
Loading

File-Level Changes

Change Details Files
Removed unused verbose parameter and related documentation from render_block
  • Deleted verbose parameter from function signature
  • Removed verbose-specific docstring entries
  • Updated docstring to state INFO-level logging only
nixie/cli.py
Fixed asyncio.subprocess references for process type and pipes
  • Changed proc annotation to use asyncio_subprocess.Process
  • Replaced stdout/stderr PIPEs with asyncio_subprocess.PIPE
nixie/cli.py
Renamed test to align with new logging behavior
  • Updated test name from test_render_block_silent_without_verbose to test_render_block_silent_when_warning_level
nixie/unittests/test_verbose.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 7, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Summary by CodeRabbit

  • Bug Fixes
    • Improved security by ensuring only specific executables are allowed when running diagram rendering commands.
  • Tests
    • Added a test to verify that unexpected executables are correctly rejected.
    • Renamed a test for clarity regarding logging behaviour at different verbosity levels.

Walkthrough

Refactor subprocess handling in nixie/cli.py by introducing an executable whitelist and centralising concurrency control within a new helper coroutine. Update type annotations and function signatures accordingly. Add a unit test for executable validation and rename an existing test for clarity.

Changes

Cohort / File(s) Change Summary
Subprocess Handling & Security
nixie/cli.py
Introduce _run_mermaid_cli coroutine for subprocess management with concurrency and executable whitelist. Update type annotations and move subprocess logic into the helper. Enforce allowed executables. Update _render_diagram to use the helper.
Test Enhancements: Executable Whitelist
nixie/unittests/test_render_diagram.py
Add async test to verify _run_mermaid_cli rejects unexpected executables with a ValueError.
Test Renaming for Clarity
nixie/unittests/test_verbose.py
Rename test function to clarify it tests silent rendering at WARNING log level; no logic changes.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant _render_diagram
    participant _run_mermaid_cli
    participant Semaphore
    participant Subprocess

    Caller->>_render_diagram: call with block, paths, sem, timeout
    _render_diagram->>_run_mermaid_cli: call with cmd, sem, path, idx, timeout
    _run_mermaid_cli->>Semaphore: acquire()
    _run_mermaid_cli->>_run_mermaid_cli: validate executable in whitelist
    _run_mermaid_cli->>Subprocess: create and run process
    _run_mermaid_cli->>Semaphore: release()
    _run_mermaid_cli-->>_render_diagram: return (success, output)
    _render_diagram-->>Caller: complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Possibly related PRs

Poem

In code we trust, with whitelists tight,
Subprocesses guarded, no rogue in sight.
Tests renamed and added, all neat and clear,
Semaphore in hand, no chaos to fear.
With diagrams rendered and errors caught,
Secure and tidy—just as it ought!
🦾✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/remove-unused-verbose-parameter

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and they look great!

Blocking issues:

  • Detected 'create_subprocess_exec' function without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)
Prompt for AI Agents
Please address the comments from this code review:
## Security Issues

### Issue 1
<location> `nixie/cli.py:164` </location>

<issue_to_address>
**security (python.lang.security.audit.dangerous-asyncio-create-exec-audit):** Detected 'create_subprocess_exec' function without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread nixie/cli.py Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 906f11d and 7562707.

📒 Files selected for processing (2)
  • nixie/cli.py (4 hunks)
  • nixie/unittests/test_verbose.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit Inference Engine (AGENTS.md)

For Python files: Pass all relevant unit and behavioral tests, pass lint checks (ruff check), adhere to formatting standards (ruff format), and pass type checking (pyright).

**/*.py: Use snake_case.py for file names, naming files for their contents (e.g., http_client.py, task_queue.py)
Use PascalCase for class names in Python files
Use snake_case for variable and function names in Python files
Use UPPER_SNAKE_CASE for module-level constants in Python files
Prefix private/internal helpers or APIs with a single underscore (_) in Python files
Use typing everywhere and maintain full static type coverage in Python files; use Pyright for type-checking
Use TypedDict or Dataclass for structured data where appropriate; for internal-only usage, prefer @DataClass(slots=True)
Avoid Any in type annotations; use Unknown, generics, or cast() when necessary, and always document why Any is acceptable if used
Be explicit with return types in public functions and class methods (e.g., use -> None, -> str, etc.)
Favor immutability in Python files; prefer tuples over lists, and frozendict or types.MappingProxyType where appropriate
Enable Ruff for linting and formatting; use Ruff to lint for performance, security, consistency, and style issues, and let Ruff handle whitespace and formatting entirely
Enforce strict mode in Pyright and treat all Pyright warnings as CI errors; use # pyright: ignore sparingly and with explanation
Avoid side effects at import time in Python modules; modules should not modify global state or perform actions on import
Use docstrings to document public functions, classes, and modules using NumPy format
Explain tricky code with inline comments for non-obvious logic or decisions in Python files

**/*.py: Use context managers to encapsulate setup and teardown logic cleanly and safely, especially for resource management (files, locks, connections, etc.), instead of manual try/finally blocks.
Use @contextmanager from contextlib for straightforward proce...

Files:

  • nixie/cli.py
  • nixie/unittests/test_verbose.py

⚙️ CodeRabbit Configuration File

**/*.py: How about the following?

- Keep cyclomatic complexity ≤ 12
- Follow single responsibility and CQRS (command/query segregation)
- Docstrings must follow the `numpy` style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.
- Move conditionals with >2 branches to predicate/helper functions
- Avoid `eval`, `exec`, `pickle`, monkey-patching, `ctypes`, unsafe shell
- Every module must begin with a triple-quoted docstring explaining its purpose, utility, and usage, including example calls if appropriate.
- Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar
- Lint suppressions:
  - Blanket `# noqa`, file-level skips, and categories are **forbidden**
  - Only narrow in-line disables (`# noqa: XYZ`) are permitted, and must be accompanied by `FIXME:` or a ticket link, and used only as a last resort.
- Use `pytest` fixtures for shared setup (`conftest.py` or `fixtures/`)
- Replace duplicate tests with `@pytest.mark.parametrize`
- Prefer `pytest-mock` or `unittest.mock` for stubs/mocks
- Use `assert …, "message"` over bare asserts
- Reflect all API/behaviour changes in `docs/` and update roadmap on completion
- Files must not exceed 400 logical lines:
  - Decompose large modules into subpackages
  - Split large `match/case` or dispatch tables by domain and collocate with targets if appropriate
  - Move bulky data (fixtures, templates) to external files for parsing at runtime
- Mutable defaults and shadowed built-ins are forbidden
- All code must have clear type hints using modern style (`A | B`, `list[str]`, `class Foo[A]:`, `type Bar = int`, etc.), with ABC imports drawn from the correct stdlib module.

Files:

  • nixie/cli.py
  • nixie/unittests/test_verbose.py
**/unittests/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.mdc)

Colocate unit tests with code using a unittests subdirectory and a test_ prefix for test files

Files:

  • nixie/unittests/test_verbose.py
**/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.mdc)

**/test_*.py: Use pytest idioms in test files: prefer fixtures over setup/teardown methods, parametrize broadly, and avoid unnecessary mocks
Group related tests using class with method names prefixed by test_ in test files

Files:

  • nixie/unittests/test_verbose.py
🪛 Ruff (0.12.2)
nixie/cli.py

96-96: Async function definition with a timeout parameter

(ASYNC109)


96-96: Trailing comma missing

Add trailing comma

(COM812)

🔇 Additional comments (6)
nixie/cli.py (5)

6-6: LGTM!

The import alias improves readability and maintains consistency with the subprocess references used elsewhere in the file.


166-167: LGTM!

The subprocess pipe constants correctly use the new alias, maintaining consistency with the import.


205-205: LGTM!

The docstring correctly reflects the updated behaviour where command lines are always logged at INFO level, removing the previous conditional logic.


180-189: Confirm all render_block invocations updated

All calls to render_block now pass exactly six parameters (block, tmpdir, cfg_path, path, idx, semaphore), matching the new signature.

Check locations:

  • nixie/cli.py: invocation in task list under with tempfile.TemporaryDirectory()…
  • nixie/unittests/test_verbose.py: two assertions calling await render_block(…)

309-320: Ignore removal suggestion of --verbose flag.

The --verbose flag correctly sets the global logging level (INFO vs WARNING) via logging.basicConfig(), and render_block uses that level to emit or suppress its command logs. Tests in nixie/unittests/test_verbose.py verify that INFO logs the command and WARNING silences it.

Likely an incorrect or invalid review comment.

nixie/unittests/test_verbose.py (1)

56-56: LGTM!

The function rename accurately reflects the test's purpose, focusing on logging level behaviour rather than a verbose flag. The test logic remains correct and validates the expected logging behaviour.

Comment thread nixie/cli.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (2)
nixie/unittests/test_run_mermaid_cli.py (1)

82-89: Strengthen coverage with parametrisation and message assertion.

Parametrise multiple disallowed executables and assert the offending executable appears in the message. Also assert no subprocess is spawned by guarding the factory.

Apply this diff within this test to parametrise:

-@pytest.mark.asyncio
-async def test_run_mermaid_cli_rejects_unexpected_executable() -> None:
-    semaphore = asyncio.Semaphore(1)
-    path = Path("doc.md")
-    cmd = ["echo", "hello"]
-    with pytest.raises(ValueError, match="Unexpected executable"):
-        await _run_mermaid_cli(cmd, semaphore, path, 1, 30.0)
+@pytest.mark.asyncio
+@pytest.mark.parametrize("exe", ["echo", "python", "sh"])
+async def test_run_mermaid_cli_rejects_unexpected_executable(exe: str, monkeypatch: pytest.MonkeyPatch) -> None:
+    semaphore = asyncio.Semaphore(1)
+    path = Path("doc.md")
+    cmd = [exe, "hello"]
+
+    # Fail the test if a subprocess is attempted (whitelist must short-circuit first).
+    async def should_not_be_called(*_args, **_kwargs):  # pragma: no cover - defensive
+        raise AssertionError("create_subprocess_exec must not be called for disallowed executables")
+    monkeypatch.setattr(asyncio, "create_subprocess_exec", should_not_be_called)
+
+    with pytest.raises(ValueError, match=rf"Unexpected executable: {exe}"):
+        await _run_mermaid_cli(cmd, semaphore, path, 1, 30.0)
nixie/cli.py (1)

153-159: Document the new ValueError in the Raises section.

Update the docstring to reflect the whitelist enforcement.

Apply this diff:

     Raises
     ------
     RuntimeError
         If the CLI exits with a non-zero status.
     FileNotFoundError
         If the CLI executable cannot be found.
+    ValueError
+        If the executable in ``cmd`` is not one of the allowed values.
♻️ Duplicate comments (2)
nixie/cli.py (2)

168-172: Resolve prior “dangerous create_subprocess_exec” audit.

Invoke create_subprocess_exec with an argument list (no shell), validate the executable via whitelist, and pipe stdio explicitly. This mitigates command injection vectors.


95-97: Add trailing comma in async signature to satisfy Ruff COM812 and keep diffs minimal.

Apply this diff:

-async def wait_for_proc(
-    proc: asyncio_subprocess.Process, path: Path, idx: int, timeout: float = 30.0
-) -> tuple[bool, bytes]:
+async def wait_for_proc(
+    proc: asyncio_subprocess.Process, path: Path, idx: int, timeout: float = 30.0,
+) -> tuple[bool, bytes]:
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7562707 and c7a837b.

📒 Files selected for processing (2)
  • nixie/cli.py (4 hunks)
  • nixie/unittests/test_run_mermaid_cli.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit Inference Engine (AGENTS.md)

For Python files: Pass all relevant unit and behavioral tests, pass lint checks (ruff check), adhere to formatting standards (ruff format), and pass type checking (pyright).

**/*.py: Use snake_case.py for file names, naming files for their contents (e.g., http_client.py, task_queue.py)
Use PascalCase for class names in Python files
Use snake_case for variable and function names in Python files
Use UPPER_SNAKE_CASE for module-level constants in Python files
Prefix private/internal helpers or APIs with a single underscore (_) in Python files
Use typing everywhere and maintain full static type coverage in Python files; use Pyright for type-checking
Use TypedDict or Dataclass for structured data where appropriate; for internal-only usage, prefer @DataClass(slots=True)
Avoid Any in type annotations; use Unknown, generics, or cast() when necessary, and always document why Any is acceptable if used
Be explicit with return types in public functions and class methods (e.g., use -> None, -> str, etc.)
Favor immutability in Python files; prefer tuples over lists, and frozendict or types.MappingProxyType where appropriate
Enable Ruff for linting and formatting; use Ruff to lint for performance, security, consistency, and style issues, and let Ruff handle whitespace and formatting entirely
Enforce strict mode in Pyright and treat all Pyright warnings as CI errors; use # pyright: ignore sparingly and with explanation
Avoid side effects at import time in Python modules; modules should not modify global state or perform actions on import
Use docstrings to document public functions, classes, and modules using NumPy format
Explain tricky code with inline comments for non-obvious logic or decisions in Python files

**/*.py: Use context managers to encapsulate setup and teardown logic cleanly and safely, especially for resource management (files, locks, connections, etc.), instead of manual try/finally blocks.
Use @contextmanager from contextlib for straightforward proce...

Files:

  • nixie/unittests/test_run_mermaid_cli.py
  • nixie/cli.py

⚙️ CodeRabbit Configuration File

**/*.py: How about the following?

- Keep cyclomatic complexity ≤ 12
- Follow single responsibility and CQRS (command/query segregation)
- Docstrings must follow the `numpy` style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.
- Move conditionals with >2 branches to predicate/helper functions
- Avoid `eval`, `exec`, `pickle`, monkey-patching, `ctypes`, unsafe shell
- Every module must begin with a triple-quoted docstring explaining its purpose, utility, and usage, including example calls if appropriate.
- Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar
- Lint suppressions:
  - Blanket `# noqa`, file-level skips, and categories are **forbidden**
  - Only narrow in-line disables (`# noqa: XYZ`) are permitted, and must be accompanied by `FIXME:` or a ticket link, and used only as a last resort.
- Use `pytest` fixtures for shared setup (`conftest.py` or `fixtures/`)
- Replace duplicate tests with `@pytest.mark.parametrize`
- Prefer `pytest-mock` or `unittest.mock` for stubs/mocks
- Use `assert …, "message"` over bare asserts
- Reflect all API/behaviour changes in `docs/` and update roadmap on completion
- Files must not exceed 400 logical lines:
  - Decompose large modules into subpackages
  - Split large `match/case` or dispatch tables by domain and collocate with targets if appropriate
  - Move bulky data (fixtures, templates) to external files for parsing at runtime
- Mutable defaults and shadowed built-ins are forbidden
- All code must have clear type hints using modern style (`A | B`, `list[str]`, `class Foo[A]:`, `type Bar = int`, etc.), with ABC imports drawn from the correct stdlib module.

Files:

  • nixie/unittests/test_run_mermaid_cli.py
  • nixie/cli.py
**/unittests/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.mdc)

Colocate unit tests with code using a unittests subdirectory and a test_ prefix for test files

Files:

  • nixie/unittests/test_run_mermaid_cli.py
**/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.mdc)

**/test_*.py: Use pytest idioms in test files: prefer fixtures over setup/teardown methods, parametrize broadly, and avoid unnecessary mocks
Group related tests using class with method names prefixed by test_ in test files

Files:

  • nixie/unittests/test_run_mermaid_cli.py
🧬 Code Graph Analysis (1)
nixie/unittests/test_run_mermaid_cli.py (1)
nixie/cli.py (1)
  • _run_mermaid_cli (141-181)
🪛 Ruff (0.12.2)
nixie/cli.py

96-96: Async function definition with a timeout parameter

(ASYNC109)


96-96: Trailing comma missing

Add trailing comma

(COM812)


165-165: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (3)
nixie/unittests/test_run_mermaid_cli.py (1)

82-89: Validate unexpected executable branch (LGTM).

Exercise the whitelist guard and assert the correct exception type/message. Keep.

nixie/cli.py (2)

6-6: Alias asyncio.subprocess for clearer typing (LGTM).

Use asyncio_subprocess consistently in annotations and PIPE constants.


209-210: Clarify logging behaviour in docs (LGTM).

State INFO-level logging without tying it to a function parameter.

Comment thread nixie/cli.py
Comment thread nixie/cli.py Outdated
leynos and others added 3 commits August 7, 2025 23:39
… centralize process handling. This fixes unresolved reference in tests and ensures safer CLI invocation paths.

💘 Generated with Crush
Co-Authored-By: Crush <crush@charm.land>
@leynos leynos force-pushed the codex/remove-unused-verbose-parameter branch from c7a837b to b8ff0a1 Compare August 7, 2025 22:47
leynos and others added 2 commits August 7, 2025 23:53
Ensure the generated command is non-empty and that its first token is one of
our allowed executables before logging or executing. This prevents logging
unvalidated or empty commands and avoids potential IndexError.

💘 Generated with Crush
Co-Authored-By: Crush <crush@charm.land>
Define ALLOWED_EXECUTABLES as a typing.Final frozenset and replace per-call
set creations in _run_mermaid_cli and _render_diagram. Improves clarity and
avoids redundant allocations.

💘 Generated with Crush
Co-Authored-By: Crush <crush@charm.land>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
nixie/cli.py (3)

38-38: Hoist and freeze allowed executables. Resolved prior review.

Define ALLOWED_EXECUTABLES as Final[frozenset[str]] at module scope to make policy explicit and avoid per-call allocation.


186-188: Validate before logging. Resolved prior review.

Perform the whitelist check before emitting the command string; also guard empty commands. This addresses the earlier logging/validation ordering concern.


108-109: Add trailing comma for multi-line signature (ruff COM812).

Comply with formatting lint and keep diffs minimal.

-async def wait_for_proc(
-    proc: asyncio_subprocess.Process, path: Path, idx: int, timeout: float = 30.0
+async def wait_for_proc(
+    proc: asyncio_subprocess.Process, path: Path, idx: int, timeout: float = 30.0,
 ) -> tuple[bool, bytes]:
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7a837b and 0ba3e7d.

📒 Files selected for processing (3)
  • nixie/cli.py (5 hunks)
  • nixie/unittests/test_render_diagram.py (2 hunks)
  • nixie/unittests/test_verbose.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit Inference Engine (AGENTS.md)

For Python files: Pass all relevant unit and behavioral tests, pass lint checks (ruff check), adhere to formatting standards (ruff format), and pass type checking (pyright).

**/*.py: Use snake_case.py for file names, naming files for their contents (e.g., http_client.py, task_queue.py)
Use PascalCase for class names in Python files
Use snake_case for variable and function names in Python files
Use UPPER_SNAKE_CASE for module-level constants in Python files
Prefix private/internal helpers or APIs with a single underscore (_) in Python files
Use typing everywhere and maintain full static type coverage in Python files; use Pyright for type-checking
Use TypedDict or Dataclass for structured data where appropriate; for internal-only usage, prefer @DataClass(slots=True)
Avoid Any in type annotations; use Unknown, generics, or cast() when necessary, and always document why Any is acceptable if used
Be explicit with return types in public functions and class methods (e.g., use -> None, -> str, etc.)
Favor immutability in Python files; prefer tuples over lists, and frozendict or types.MappingProxyType where appropriate
Enable Ruff for linting and formatting; use Ruff to lint for performance, security, consistency, and style issues, and let Ruff handle whitespace and formatting entirely
Enforce strict mode in Pyright and treat all Pyright warnings as CI errors; use # pyright: ignore sparingly and with explanation
Avoid side effects at import time in Python modules; modules should not modify global state or perform actions on import
Use docstrings to document public functions, classes, and modules using NumPy format
Explain tricky code with inline comments for non-obvious logic or decisions in Python files

**/*.py: Use context managers to encapsulate setup and teardown logic cleanly and safely, especially for resource management (files, locks, connections, etc.), instead of manual try/finally blocks.
Use @contextmanager from contextlib for straightforward proce...

Files:

  • nixie/unittests/test_render_diagram.py
  • nixie/unittests/test_verbose.py
  • nixie/cli.py

⚙️ CodeRabbit Configuration File

**/*.py: How about the following?

- Keep cyclomatic complexity ≤ 12
- Follow single responsibility and CQRS (command/query segregation)
- Docstrings must follow the `numpy` style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.
- Move conditionals with >2 branches to predicate/helper functions
- Avoid `eval`, `exec`, `pickle`, monkey-patching, `ctypes`, unsafe shell
- Every module must begin with a triple-quoted docstring explaining its purpose, utility, and usage, including example calls if appropriate.
- Comments and docs must follow en-GB-oxendict (-ize / -our) spelling and grammar
- Lint suppressions:
  - Blanket `# noqa`, file-level skips, and categories are **forbidden**
  - Only narrow in-line disables (`# noqa: XYZ`) are permitted, and must be accompanied by `FIXME:` or a ticket link, and used only as a last resort.
- Use `pytest` fixtures for shared setup (`conftest.py` or `fixtures/`)
- Replace duplicate tests with `@pytest.mark.parametrize`
- Prefer `pytest-mock` or `unittest.mock` for stubs/mocks
- Use `assert …, "message"` over bare asserts
- Reflect all API/behaviour changes in `docs/` and update roadmap on completion
- Files must not exceed 400 logical lines:
  - Decompose large modules into subpackages
  - Split large `match/case` or dispatch tables by domain and collocate with targets if appropriate
  - Move bulky data (fixtures, templates) to external files for parsing at runtime
- Mutable defaults and shadowed built-ins are forbidden
- All code must have clear type hints using modern style (`A | B`, `list[str]`, `class Foo[A]:`, `type Bar = int`, etc.), with ABC imports drawn from the correct stdlib module.

Files:

  • nixie/unittests/test_render_diagram.py
  • nixie/unittests/test_verbose.py
  • nixie/cli.py
**/unittests/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.mdc)

Colocate unit tests with code using a unittests subdirectory and a test_ prefix for test files

Files:

  • nixie/unittests/test_render_diagram.py
  • nixie/unittests/test_verbose.py
**/test_*.py

📄 CodeRabbit Inference Engine (.rules/python-00.mdc)

**/test_*.py: Use pytest idioms in test files: prefer fixtures over setup/teardown methods, parametrize broadly, and avoid unnecessary mocks
Group related tests using class with method names prefixed by test_ in test files

Files:

  • nixie/unittests/test_render_diagram.py
  • nixie/unittests/test_verbose.py
🪛 Ruff (0.12.2)
nixie/cli.py

108-108: Async function definition with a timeout parameter

(ASYNC109)


108-108: Trailing comma missing

Add trailing comma

(COM812)


127-127: Async function definition with a timeout parameter

(ASYNC109)


130-130: Avoid specifying long messages outside the exception class

(TRY003)


187-187: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (4)
nixie/unittests/test_verbose.py (1)

56-56: Rename aligns with behaviour (INFO vs WARNING). Approve.

Keep the test name focused on logging level rather than a verbose flag; it matches the current design.

nixie/unittests/test_render_diagram.py (1)

78-84: Validate executable whitelist via negative test. Approve.

Assert ValueError for an unexpected executable to lock down the whitelist; this complements runtime safeguards.

nixie/cli.py (2)

16-16: Alias asyncio.subprocess for clearer typing. Approve.

Use asyncio_subprocess in annotations and calls to improve clarity and Pyright compatibility.


190-191: Centralise subprocess handling in helper. Approve.

Delegate process lifecycle to _run_mermaid_cli to keep _render_diagram focused on I/O and error reporting.

Comment thread nixie/cli.py
Comment on lines +122 to +140
async def _run_mermaid_cli(
cmd: list[str],
sem: asyncio.Semaphore,
path: Path,
idx: int,
timeout: float,
) -> tuple[bool, bytes]:
if not cmd or cmd[0] not in ALLOWED_EXECUTABLES:
raise ValueError(f"Unexpected executable: {cmd[0] if cmd else ''}")

async with sem:
proc = await asyncio.create_subprocess_exec( # nosemgrep: python.lang.security.audit.dangerous-asyncio-create-exec-audit
*cmd,
stdout=asyncio_subprocess.PIPE,
stderr=asyncio_subprocess.PIPE,
)

return await wait_for_proc(proc, path, idx, timeout)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Aug 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Hold the semaphore until the process completes.

Do not release the semaphore immediately after spawning the process; this defeats concurrency limiting for long-running tasks. Guard both creation and wait within the same async with block.

 async def _run_mermaid_cli(
@@
-    async with sem:
-        proc = await asyncio.create_subprocess_exec(  # nosemgrep: python.lang.security.audit.dangerous-asyncio-create-exec-audit
-            *cmd,
-            stdout=asyncio_subprocess.PIPE,
-            stderr=asyncio_subprocess.PIPE,
-        )
-
-    return await wait_for_proc(proc, path, idx, timeout)
+    async with sem:
+        proc = await asyncio.create_subprocess_exec(  # nosemgrep: python.lang.security.audit.dangerous-asyncio-create-exec-audit
+            *cmd,
+            stdout=asyncio_subprocess.PIPE,
+            stderr=asyncio_subprocess.PIPE,
+        )
+        return await wait_for_proc(proc, path, idx, timeout)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async def _run_mermaid_cli(
cmd: list[str],
sem: asyncio.Semaphore,
path: Path,
idx: int,
timeout: float,
) -> tuple[bool, bytes]:
if not cmd or cmd[0] not in ALLOWED_EXECUTABLES:
raise ValueError(f"Unexpected executable: {cmd[0] if cmd else ''}")
async with sem:
proc = await asyncio.create_subprocess_exec( # nosemgrep: python.lang.security.audit.dangerous-asyncio-create-exec-audit
*cmd,
stdout=asyncio_subprocess.PIPE,
stderr=asyncio_subprocess.PIPE,
)
return await wait_for_proc(proc, path, idx, timeout)
async def _run_mermaid_cli(
cmd: list[str],
sem: asyncio.Semaphore,
path: Path,
idx: int,
timeout: float,
) -> tuple[bool, bytes]:
if not cmd or cmd[0] not in ALLOWED_EXECUTABLES:
raise ValueError(f"Unexpected executable: {cmd[0] if cmd else ''}")
async with sem:
proc = await asyncio.create_subprocess_exec( # nosemgrep: python.lang.security.audit.dangerous-asyncio-create-exec-audit
*cmd,
stdout=asyncio_subprocess.PIPE,
stderr=asyncio_subprocess.PIPE,
)
return await wait_for_proc(proc, path, idx, timeout)
🧰 Tools
🪛 Ruff (0.12.2)

127-127: Async function definition with a timeout parameter

(ASYNC109)


130-130: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
In nixie/cli.py around lines 122 to 140, the semaphore is released immediately
after starting the subprocess, which allows more concurrent tasks than intended.
To fix this, move the await wait_for_proc call inside the async with sem block
so that the semaphore is held until the subprocess completes, properly limiting
concurrency for long-running tasks.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai new GitHub issue, please

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

@leynos I've created a GitHub issue to track the semaphore concurrency problem identified in the _run_mermaid_cli function. The issue has been assigned to you and includes references to this PR and comment thread.


🏁 Script executed:

#!/bin/bash
gh issue create \
  --title "Semaphore released too early in _run_mermaid_cli function defeats concurrency limiting" \
  --body "## Issue Description

The \`_run_mermaid_cli\` function in \`nixie/cli.py\` releases the semaphore immediately after spawning the subprocess, which defeats the concurrency limiting mechanism for long-running tasks.

## Current Behaviour

The semaphore is held only during subprocess creation but released before waiting for the process to complete:

\`\`\`python
async with sem:
    proc = await asyncio.create_subprocess_exec(...)

return await wait_for_proc(proc, path, idx, timeout)
\`\`\`

## Expected Behaviour

The semaphore should be held until the subprocess completes to properly limit concurrency:

\`\`\`python
async with sem:
    proc = await asyncio.create_subprocess_exec(...)
    return await wait_for_proc(proc, path, idx, timeout)
\`\`\`

## Impact

This allows more concurrent tasks than intended, potentially overwhelming system resources when processing multiple diagrams.

## References

- PR: https://github.com/leynos/nixie/pull/11
- Comment: https://github.com/leynos/nixie/pull/11#discussion_r2261612240
- Requested by: @leynos" \
  --assignee leynos

Length of output: 1220


@leynos Perfect! I've successfully created GitHub issue #16 for the semaphore concurrency problem. You can view it at: #16

The issue has been assigned to you and includes a detailed description of the problem, the current vs expected behaviour, impact assessment, and references back to this PR and comment thread.


🧬 Code Graph Analysis Results

Relevant snippet from nixie/unittests/test_get_mmdc_cmd.py lines 22-25:

def which(cmd: str) -> str | None:
        if cmd == "bun":
            return "/usr/bin/bun"
        return None

This snippet provides a mock implementation of the which function that returns a path for the "bun" executable and None otherwise. It is relevant for understanding or testing the executable selection logic in nixie/cli.py where shutil.which is used to detect available executables (mmdc, bun, npx).

Comment thread nixie/unittests/test_render_diagram.py
@leynos leynos merged commit 17af305 into main Aug 7, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant