Skip to content

Disable sandbox for root mmdc runs#20

Merged
leynos merged 2 commits intomainfrom
codex/run-mmdc-with-puppeteer-as-root
Aug 13, 2025
Merged

Disable sandbox for root mmdc runs#20
leynos merged 2 commits intomainfrom
codex/run-mmdc-with-puppeteer-as-root

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Aug 13, 2025

Summary

  • add root-aware Puppeteer config that disables sandbox for mmdc
  • invoke mmdc with --puppeteerConfigFile when config is present
  • document root-only sandbox disabling

Testing

  • make fmt
  • make check-fmt
  • make lint
  • make typecheck
  • make test
  • make nixie
  • make markdownlint (fails: reference links missing in .rules/python-pyproject.md)

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

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.

Sorry @leynos, you have reached your 24-hour rate limit for Sourcery. Please try again later

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 13, 2025

Summary by CodeRabbit

  • New Features
    • When run as root, the app now automatically disables the Puppeteer sandbox for diagram rendering, improving reliability in privileged environments. Non-root runs are unaffected.
  • Tests
    • Added unit and integration tests covering root/non-root behaviour and optional Puppeteer configuration flow.
  • Documentation
    • Changelog updated to describe the new root-specific behaviour and sandbox settings.

Walkthrough

Introduce optional Puppeteer config generation based on effective UID. When run as root, create a temporary JSON config containing --no-sandbox and --disable-setuid-sandbox and pass it to mmdc; when not root, skip the config. Update CLI signatures and tests to accept an optional cfg_path.

Changes

Cohort / File(s) Summary of changes
CLI: Optional Puppeteer config and signature updates
nixie/cli.py
Make create_puppeteer_config yield `Path
Unit tests: Puppeteer config behaviour
nixie/unittests/test_puppeteer_config.py
Add tests that monkeypatch os.geteuid() to validate create_puppeteer_config() returns a temp JSON with the two args for root, and returns None for non-root.
Integration tests: Signature alignment
tests/integration/conftest.py, tests/integration/test_cli_behavior.py
Relax test stub/mock cfg_path annotations from Path to `Path
Docs: Changelog
docs/CHANGELOG.md
Add Unreleased entry noting that when run as root, nixie invokes mmdc with a Puppeteer config disabling the sandbox via --no-sandbox and --disable-setuid-sandbox.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CLI as nixie.cli:main
  participant PPC as create_puppeteer_config
  participant MMDC as mmdc

  User->>CLI: invoke
  CLI->>PPC: create_puppeteer_config()
  alt running as root
    PPC-->>CLI: Path to temp puppeteer config
  else non-root
    PPC-->>CLI: None
  end
  CLI->>MMDC: spawn with args (include --puppeteerConfigFile when Path)
  MMDC-->>CLI: SVG output / status
  CLI-->>User: return results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

Run as root, write a JSON note, two flags to set,
Run as non-root, leave the temp file unmet.
Spawn mmdc with care or leave it be,
Tests assert both modes and set the flow free.
Celebrate the tweak—diagrams render correctly!


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a39b925 and 9f1fb5e.

📒 Files selected for processing (1)
  • nixie/cli.py (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.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

⚙️ CodeRabbit Configuration File

**/*.py: - 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
🔇 Additional comments (3)
nixie/cli.py (3)

84-96: LGTM: implement root-aware Puppeteer config correctly

Yield None for non-root and a temporary config for root. The control flow and docstring read cleanly and align with the PR objective.


124-126: LGTM: append Puppeteer config flag only when cfg_path is set

Gate the flag correctly to avoid passing an empty argument when not running as root.


182-201: LGTM: thread cfg_path through the private rendering path

Propagate the optional config cleanly; the docstring is clear for a private helper.

✨ 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/run-mmdc-with-puppeteer-as-root

🪧 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.
    • 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.
  • 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 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/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

5 files reviewed, no comments

Edit Code Review Bot Settings | Greptile

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

🔭 Outside diff range comments (6)
nixie/cli.py (5)

147-152: Catch the correct timeout exception from asyncio.wait_for.

asyncio.wait_for raises asyncio.TimeoutError, not the built-in TimeoutError. The except block will not execute as written.

Apply this diff to handle timeouts correctly:

-    try:
-        _, stderr = await asyncio.wait_for(proc.communicate(), timeout)
-    except TimeoutError:
+    try:
+        _, stderr = await asyncio.wait_for(proc.communicate(), timeout)
+    except asyncio.TimeoutError:
         proc.kill()
         await proc.wait()
         print(f"{path}: diagram {idx} timed out", file=sys.stderr)
         return (False, b"")

221-241: Deduplicate subprocess logic by reusing _run_mermaid_cli.

Avoid maintaining two separate subprocess execution paths and ALLOWED_EXECUTABLES checks. Reuse _run_mermaid_cli to reduce complexity and keep behaviour consistent.

Apply this diff to streamline _render_diagram:

-    cmd = get_mmdc_cmd(mmd, svg, cfg_path)
-    if not cmd or cmd[0] not in ALLOWED_EXECUTABLES:
-        raise UnexpectedExecutableError(cmd[0] if cmd else "")
-    LOGGER.info(shlex.join(cmd))
-
-    async with semaphore:
-        # nosemgrep: python.lang.security.audit.dangerous-asyncio-create-exec-audit
-        proc = await asyncio.create_subprocess_exec(
-            *cmd,
-            stdout=asyncio.subprocess.PIPE,
-            stderr=asyncio.subprocess.PIPE,
-        )
-        success, stderr = await wait_for_proc(proc, path, idx, timeout)
+    cmd = get_mmdc_cmd(mmd, svg, cfg_path)
+    LOGGER.info(shlex.join(cmd))
+    success, stderr = await _run_mermaid_cli(cmd, semaphore, path, idx, timeout)

230-232: Use the same asyncio_subprocess alias for PIPEs.

Align PIPE usage with the asyncio_subprocess alias already imported to keep consistency.

Apply this diff:

-            stdout=asyncio.subprocess.PIPE,
-            stderr=asyncio.subprocess.PIPE,
+            stdout=asyncio_subprocess.PIPE,
+            stderr=asyncio_subprocess.PIPE,

292-304: Fix isinstance usage for exception re-raising.

Using a PEP 604 union in isinstance raises TypeError at runtime. Use a tuple of types.

Apply this diff:

-    except Exception as exc:
-        if isinstance(exc, KeyboardInterrupt | SystemExit):
+    except Exception as exc:
+        if isinstance(exc, (KeyboardInterrupt, SystemExit)):
             raise
         LOGGER.exception(
             "%s: unexpected error in diagram %s",
             path,
             idx,
         )

292-304: Handle NoNodeEnvironmentAvailableError explicitly for clearer UX.

Emit a targeted message when no supported node environment is present, instead of the generic “unexpected error” log.

Apply this diff:

     try:
         await _render_diagram(block, tmpdir, cfg_path, path, idx, semaphore, timeout)
-    except FileNotFoundError as exc:
+    except FileNotFoundError as exc:
         cli = exc.filename or "mmdc"
         LOGGER.exception(
             (
                 "Error: '%s' not found. Install Node.js with npx or Bun to use "
                 "@mermaid-js/mermaid-cli."
             ),
             cli,
         )
+    except NoNodeEnvironmentAvailableError:
+        LOGGER.exception(
+            "No supported node environment found. Install mmdc directly, or install "
+            "Node.js (npx) or Bun to use @mermaid-js/mermaid-cli."
+        )
nixie/unittests/test_puppeteer_config.py (1)

1-29: Verify temporary file cleanup after context exit.

Assert that the generated config file is removed after leaving the context. This guards against temp-file leaks.

Apply this diff:

@@
-from nixie.cli import create_puppeteer_config
+from pathlib import Path
+from nixie.cli import create_puppeteer_config
@@
-def test_create_puppeteer_config_as_root(monkeypatch: pytest.MonkeyPatch) -> None:
+def test_create_puppeteer_config_as_root(monkeypatch: pytest.MonkeyPatch) -> None:
     """Include sandbox-disabling args when running as root."""
     monkeypatch.setattr(os, "geteuid", lambda: 0)
-    with create_puppeteer_config() as cfg:
-        assert cfg is not None
-        data = json.loads(cfg.read_text())
+    path: Path | None = None
+    with create_puppeteer_config() as cfg:
+        assert cfg is not None
+        path = cfg
+        data = json.loads(cfg.read_text())
         assert data["args"] == ["--no-sandbox", "--disable-setuid-sandbox"]
+    assert path is not None and not path.exists()
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c1d8b98 and a39b925.

📒 Files selected for processing (5)
  • docs/CHANGELOG.md (1 hunks)
  • nixie/cli.py (8 hunks)
  • nixie/unittests/test_puppeteer_config.py (1 hunks)
  • tests/integration/conftest.py (1 hunks)
  • tests/integration/test_cli_behavior.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.md

📄 CodeRabbit Inference Engine (AGENTS.md)

For Markdown files (.md only): Pass lint checks (markdownlint filename.md).

Files:

  • docs/CHANGELOG.md

⚙️ CodeRabbit Configuration File

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/CHANGELOG.md
{**/README.md,**/docs/**}

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

Colocate documentation by keeping README.md or docs/ near reusable packages and include usage examples

Files:

  • docs/CHANGELOG.md
**/*.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_puppeteer_config.py
  • tests/integration/conftest.py
  • tests/integration/test_cli_behavior.py
  • nixie/cli.py

⚙️ CodeRabbit Configuration File

**/*.py: - 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_puppeteer_config.py
  • tests/integration/conftest.py
  • tests/integration/test_cli_behavior.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_puppeteer_config.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_puppeteer_config.py
  • tests/integration/test_cli_behavior.py
tests/integration/test_*.py

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

Structure integration tests separately in tests/integration/ directories

Files:

  • tests/integration/test_cli_behavior.py
🧬 Code Graph Analysis (1)
nixie/unittests/test_puppeteer_config.py (1)
nixie/cli.py (2)
  • cli (401-411)
  • create_puppeteer_config (84-106)
🔇 Additional comments (8)
docs/CHANGELOG.md (1)

9-10: Document the root-only sandbox exception clearly.

The entry is accurate and aligns with the implementation. Keep it as-is.

nixie/cli.py (5)

84-96: Introduce root-aware Puppeteer config via context manager.

The context manager pattern and optional return (Path | None) are sound. The fallback for platforms without os.geteuid is sensible.


98-98: Write the expected Puppeteer args JSON.

Serialise the exact arguments required by mmdc; this matches the documented intent.


109-127: Pass Puppeteer config flag only when present.

Conditionally append --puppeteerConfigFile. This prevents passing a bogus path on non-root runs.


324-326: Propagate optional cfg_path through check_file.

Widen the type to Path | None to match callers and the new context manager semantics.


352-358: Create the config once and reuse per run.

Generate the Puppeteer config once per CLI invocation and pass through; this is efficient and correct.

tests/integration/conftest.py (1)

18-18: Accept optional cfg_path in the stub.

Match the updated signature across the codebase to prevent type-checking and runtime mismatches.

tests/integration/test_cli_behavior.py (1)

149-157: Align mock signature with optional cfg_path.

Allow None to thread through to the original check_file. Keep the rest of the test unchanged.

Comment thread nixie/cli.py
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, no comments

Edit Code Review Bot Settings | Greptile

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Aug 13, 2025

@coderabbitai starting in Python 3.10 and continuing in 3.13, you can use the PEP 604 pipe-style union syntax inside isinstance and issubclass checks.

That means the following is valid in Python 3.13:

# Python 3.13
def foo(x: int | str) -> None:
    if isinstance(x, int | str):  # works
        print("int or str")

Under the hood, int | str produces a types.UnionType object, and isinstance/issubclass were updated in Python 3.10 to recognise it alongside typing.Union.

Two small gotchas to be aware of:

  • This only works with class types and typing special forms that are also valid at runtime (e.g., list[int] is fine). It won’t work with typing.Any, typing.TypeVar, or other static-only constructs.
  • Mixing with Optional is fine — str | None works both in annotations and in runtime checks.

If you want, I can show you a table of exactly what kinds of PEP 604 types are valid in an isinstance call in Python 3.13.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 13, 2025

Tip

For best results, initiate chat on the files or code changes.

@leynos Thank you for this excellent explanation of PEP 604 union syntax support in isinstance and issubclass checks! This is very valuable information that will help with future code reviews.

The distinction between what works at runtime versus annotation-only constructs is particularly important to understand. I'd definitely appreciate seeing that table of valid PEP 604 types for isinstance calls in Python 3.13 - it would provide a comprehensive reference for when reviewing code that uses these patterns.

This knowledge will be especially useful when reviewing code that combines modern type annotations with runtime type checking.


✏️ Learnings added
Learnt from: leynos
URL: https://github.com/leynos/nixie/pull/20

Timestamp: 2025-08-13T06:23:04.670Z
Learning: Starting in Python 3.10, PEP 604 pipe-style union syntax (e.g., `int | str`) can be used in isinstance() and issubclass() checks, not just in type annotations. Under the hood, `int | str` creates a `types.UnionType` object that isinstance/issubclass recognize alongside `typing.Union`.

Learnt from: leynos
URL: https://github.com/leynos/nixie/pull/20

Timestamp: 2025-08-13T06:23:04.670Z
Learning: PEP 604 isinstance support gotchas: (1) Only works with class types and typing special forms valid at runtime (e.g., `list[int]` works), but won't work with static-only constructs like `typing.Any` or `typing.TypeVar`. (2) Mixing with Optional works fine - `str | None` works in both annotations and runtime checks.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@leynos leynos merged commit 9a91927 into main Aug 13, 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