Skip to content

Conversation

@Asifdotexe
Copy link
Owner

@Asifdotexe Asifdotexe commented Jan 10, 2026

The goal of this pull request was to ensure that we added support for break, continue and labelled the true and false branching of the conditional statement

Additionally we also pivoted to using poetry and restructured the directory structure for it

Performed code linting and cleanup, improved docstring coverage and added empathic block comments for my future miserable self as I will end up forgetting why I wrote that logic in that way in the first place

Also added TODO comment to mark additional items (probably worth looking into a bot to handle the todo?)

Adding a testing workflow to github actions

Summary by CodeRabbit

  • New Features

    • Interactive Streamlit app for generating Python-to-Graphviz flowcharts, plus a CLI entry.
  • Bug Fixes

    • Improved syntax validation and clearer error messages; robust asset handling and adaptive download options when Graphviz is unavailable.
  • Documentation

    • Expanded "Getting Started" guide with Poetry-based setup and Graphviz installation notes.
  • Chores / CI

    • Added tests, CI workflow, pre-commit and linter config; project packaging migrated to Poetry.

✏️ Tip: You can customize this high-level summary in your review settings.

@Asifdotexe Asifdotexe self-assigned this Jan 10, 2026
@Asifdotexe Asifdotexe linked an issue Jan 10, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 10, 2026

📝 Walkthrough

Walkthrough

Adds Poetry packaging, CI, and pre-commit hooks; removes Flake8 config; introduces a new AST-based flowchart generator (serpent/core.py), a Streamlit app entry (serpent/app.py), tests, README updates, and moves/refactors the legacy generator.

Changes

Cohort / File(s) Summary
Packaging & Tooling
pyproject.toml, packages.txt
Migrate to Poetry (tool.poetry), update metadata, deps, dev groups, CLI script; packages.txt newline normalization.
CI & Pre-commit
.github/workflows/test.yml, .pre-commit-config.yaml
Add GitHub Actions test workflow and pre-commit hooks (black, isort, pylint, checks).
Linting & Config
.flake8, .pylintrc, .streamlit/config.toml
Remove .flake8; add .pylintrc (max-line-length=120); fix Streamlit theme font typo.
Docs & Ignore
README.md, .gitignore
Rewrite Getting Started to use Poetry and Graphviz instructions; .gitignore unchanged functionally.
Package Init
serpent/__init__.py
Expose generate_graphviz_flowchart at package level and add module docstring.
Streamlit App
serpent/app.py
New Streamlit UI entrypoint: AST validation, calls generator, asset fallbacks, conditional PNG vs DOT download depending on dot availability, UI layout, and main() entry.
Core Flowchart Generator
serpent/core.py
New PythonFlowchartGV AST visitor and generate_graphviz_flowchart() producing Graphviz Digraphs; SyntaxError rendered as error graph.
Legacy Generator (refactor/removed)
serpent_scripts/serpent_graphviz.py, serpent_scripts/__init__.py
Legacy AST generator removed from serpent_scripts and docstring trimmed; functionality moved/refactored into serpent/core.py.
Tests
tests/test_flowchart.py, tests/test_app_smoke.py
Add unit tests for flowchart generation (linear, branches, loops, syntax error) and Streamlit app startup smoke test.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Browser
  participant StreamlitApp as serpent.app
  participant Core as serpent.core
  participant Graphviz as dot

  User->>Browser: Open app, paste code, click Generate
  Browser->>StreamlitApp: UI event with code string
  StreamlitApp->>Core: call generate_graphviz_flowchart(code_str, title)
  Core->>Core: parse AST & traverse (PythonFlowchartGV)
  Core->>Graphviz: produce DOT source (Digraph)
  alt dot installed
    StreamlitApp->>Graphviz: render PNG from DOT
    Graphviz-->>StreamlitApp: PNG bytes
    StreamlitApp-->>Browser: display PNG and offer PNG download
  else dot missing
    StreamlitApp-->>Browser: display DOT source and offer DOT download with warning
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

documentation

Poem

🐇 I nibble AST leaves in the night,
I hop nodes into shapes just right,
Poetry packs my tiny crate,
Graphviz paints each branching fate,
Pre-commit keeps my whiskers neat.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: refactoring the project structure to use Poetry and enhancing the flowchart capability with better control flow handling (break, continue, labeled branches).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 460d799 and a09b216.

📒 Files selected for processing (2)
  • serpent_scripts/__init__.py
  • serpent_scripts/serpent_graphviz.py
💤 Files with no reviewable changes (2)
  • serpent_scripts/init.py
  • serpent_scripts/serpent_graphviz.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
serpent_scripts/serpent_graphviz.py (1)

362-390: Remove duplicate code from serpent_scripts/serpent_graphviz.py.

The generate_graphviz_flowchart function and PythonFlowchartGV class are duplicated in this file and serpent/core.py. Since serpent/__init__.py imports from serpent.core and the serpent_scripts/serpent_graphviz.py module is not imported anywhere in the codebase, this file contains unused duplicate code that should be removed.

README.md (1)

10-10: Fix the README image reference path on line 10.

examples/serpent_banner_white_bg.png does not exist; the file is located in assets/serpent_banner_white_bg.png. Update the reference on line 10 to use the correct path. (Lines 53 and 69 reference valid image files in the examples directory and do not need changes.)

🤖 Fix all issues with AI agents
In @.pre-commit-config.yaml:
- Around line 10-29: The isort hook in .pre-commit-config.yaml is using an
invalid rev "6.1.0"; update the isort repo entry (the hook with id: isort and
name: isort (python)) to reference a valid version on PyPI such as "6.0.1" or
"7.0.0" by replacing the rev value accordingly so the pre-commit hook resolves
correctly.

In @packages.txt:
- Line 1: Delete the legacy packages.txt file (which currently lists "graphviz")
from the repository; ensure no build scripts, CI config, or deployment manifests
reference packages.txt (search for "packages.txt" and remove any references),
and rely on the existing pyproject.toml/poetry.lock entries (e.g., graphviz) for
dependency management instead.

In @serpent/app.py:
- Around line 206-213: Replace the blind top-level exception handler that shows
raw exception details to users: in the except Exception block (and keep the
SyntaxError user message unchanged), log the full traceback server-side using
logging.exception(...) or traceback.format_exc() before returning a user-safe
message via st.error without the raw exception text; specifically update the
generic except Exception handler that currently calls st.error(f"...{e}") to
call logging.exception("Unexpected error in <function/module>") and then
st.error("An unexpected error occurred. Please try again or contact support.")
so diagnostics are recorded but internal details aren't exposed to users.
- Around line 179-193: The TODO comment indicating PNG download is unimplemented
is stale; remove or replace it with an accurate comment describing that when
shutil.which("dot") is truthy the code generates PNG bytes via
graph.pipe(format="png") and provides a download via st.download_button
(file_name uses chart_title); update the comment to reflect the implemented PNG
download behavior or delete the TODO so future readers aren't confused.
- Around line 137-147: Sanitize chart_title before using it to build file_name:
normalize to lowercase, remove or replace unsafe characters (e.g., strip path
separators, control chars), collapse whitespace to single hyphens/underscores,
truncate to a safe max length, and if the result is empty use a default like
"flowchart" (optionally append a timestamp/uuid for uniqueness); then construct
file_name using that sanitized slug + proper extension (e.g., .png/.dot). Apply
this change to all places that create file_name from chart_title (the file_name
assignments that currently use chart_title.replace(...).lower()) so both PNG and
DOT exports are safe.

In @serpent/core.py:
- Around line 273-301: The visitor methods visit_Break and visit_Continue
declare a parameter named node that is unused, triggering Ruff ARG002; rename
the unused parameter to _node in both method signatures (visit_Break(self,
_node: ast.Break) and visit_Continue(self, _node: ast.Continue)) so the linter
knows it’s intentionally unused, or alternatively add an explicit del node or a
trailing comment pragma, but prefer renaming to _node to be concise and
consistent with other visitor methods.
- Around line 136-201: The True/False labels are lost when a branch creates no
nodes because visit_If relies on the global next_edge_label consumed by the
first created node; change the flow to propagate edge labels with source tuples
instead: modify visit_If to set branch starters as (decision_node_id,"True") and
(decision_node_id,"False") when a branch may produce no nodes, collect
last_nodes as (node_id,label) pairs instead of raw ids, and update
node-creation/connection logic (the code path in new_node creation and consumers
of self.last_nodes such as visit_Pass/visit_Expr and places that currently set
self.last_nodes = [decision_node_id]) to accept and consume a tuple label when
present (clearing the label only when it's applied), so edge labels survive
empty branches and are attached to the merge edges.
- Around line 218-272: The loop handler can leave next_edge_label="True" unused
for empty bodies and passes None as an edge label; change the True-path seed to
use a tuple so the "True" label is attached even when the body yields no nodes
(set self.last_nodes = [(condition_node, "True")] instead of [condition_node]),
and when creating back-edges with self.graph.edge(src, condition_node,
label=lbl) only include the label argument if lbl is not None/empty (e.g., call
self.graph.edge(src, condition_node) when lbl is None or use "" instead), also
keep the existing loop_stack usage (loop_stack entries, loop_ctx,
condition_node) and ensure next_edge_label is reset as before.
🧹 Nitpick comments (9)
.pylintrc (1)

1-5: Restructure .pylintrc to use proper INI format or consolidate configuration into pyproject.toml.

The .pylintrc file uses non-standard syntax. The line pylint: max-line-length=120 is a directive comment format (typically used inline in .py files), not valid in a configuration file. A proper .pylintrc requires INI format with section headers such as [DESIGN] or [MESSAGES CONTROL]. Alternatively, since the project uses Poetry, consider consolidating pylint settings into pyproject.toml under [tool.pylint] to maintain a single source of tool configuration.

.github/workflows/test.yml (2)

14-19: Update GitHub Actions to current versions.

The actions/checkout@v3 and actions/setup-python@v4 are outdated. Using older versions may cause compatibility issues with newer GitHub Actions runners.

♻️ Proposed fix
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4

       - name: Set up Python
-        uses: actions/setup-python@v4
+        uses: actions/setup-python@v5
         with:
           python-version: "3.10"

21-29: Consider adding dependency caching for faster CI runs.

The workflow reinstalls all dependencies on every run. Adding Poetry caching can significantly speed up CI execution.

♻️ Example with caching
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"
          cache: 'pip'

      - name: Install Poetry
        run: pip install poetry

      - name: Cache Poetry dependencies
        uses: actions/cache@v4
        with:
          path: ~/.cache/pypoetry
          key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
          restore-keys: |
            ${{ runner.os }}-poetry-

      - name: Install dependencies
        run: poetry install

      - name: Run tests
        run: poetry run pytest tests/
serpent_scripts/serpent_graphviz.py (1)

69-75: Consider using a parameter instead of instance state for edge labels.

The next_edge_label pattern using getattr/setattr is a bit fragile. If a visit method forgets to clear it, labels could leak to unintended edges. Consider passing the edge label explicitly through visit calls or documenting this invariant clearly.

tests/test_flowchart.py (1)

44-68: Consider expanding test coverage.

The current tests provide good baseline coverage. Consider adding tests for:

  • continue statement handling
  • elif chains (nested else-if)
  • for loops
  • Nested loops with break/continue
  • Multiple functions in one code block

Example additional test:

def test_continue_statement():
    """Test continue statement in loop."""
    code = """
while True:
    if x:
        continue
    print("After continue check")
"""
    graph = generate_graphviz_flowchart(code)
    source = graph.source
    assert "continue" in source
    assert "While: True" in source
README.md (1)

71-137: Align “Development / Linter” commands with Poetry-based workflow.

Since “Getting Started” is Poetry-first (Line 84-116), “Running the Linter” (Line 134-137) should likely be poetry run flake8 . (or whatever tool is actually configured via pre-commit) to avoid requiring a global install.

serpent/core.py (2)

118-135: Skip visiting unreachable statements after flow terminators (return/break/continue).

After visit_Return/visit_Break/visit_Continue, self.last_nodes is set to [] (Line 284-285, 298-299, 310-311). Continuing to visit() subsequent statements in the same block will create disconnected nodes (because new_node() will have no sources). If you want unreachable code omitted (typical flowchart expectation), break out of the statement loop when last_nodes becomes empty.

Also applies to: 158-176, 242-248


364-392: Consider returning SyntaxError details from the public API (optional).

generate_graphviz_flowchart swallows the exception and returns a generic “Syntax Error” node (Line 375-387). The Streamlit app currently re-parses just to show details. Consider capturing the exception message in the error node (or offering an option to surface details) so callers don’t need to parse twice.

serpent/app.py (1)

22-31: Asset fallback is good, but consider packaging-safe asset loading for installs.

assets_dir = Path(__file__).parent.parent / "assets" (Line 22) is fine for a repo checkout, but can break depending on packaging/layout. If this is intended to work when installed as a package, prefer importlib.resources for asset retrieval (and keep the current fallback).

Also applies to: 33-38

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8974201 and fda3153.

⛔ Files ignored due to path filters (4)
  • assets/serpent_banner_transparent.png is excluded by !**/*.png
  • assets/serpent_banner_white_bg.png is excluded by !**/*.png
  • assets/serpent_logo_transparent.png is excluded by !**/*.png
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • .flake8
  • .github/workflows/test.yml
  • .gitignore
  • .pre-commit-config.yaml
  • .pylintrc
  • .streamlit/config.toml
  • README.md
  • packages.txt
  • pyproject.toml
  • serpent/__init__.py
  • serpent/app.py
  • serpent/core.py
  • serpent_scripts/serpent_graphviz.py
  • tests/test_flowchart.py
💤 Files with no reviewable changes (1)
  • .flake8
🧰 Additional context used
🧬 Code graph analysis (2)
serpent/__init__.py (2)
serpent/core.py (1)
  • generate_graphviz_flowchart (364-392)
serpent_scripts/serpent_graphviz.py (1)
  • generate_graphviz_flowchart (362-390)
serpent/app.py (2)
serpent/core.py (1)
  • generate_graphviz_flowchart (364-392)
serpent_scripts/serpent_graphviz.py (1)
  • generate_graphviz_flowchart (362-390)
🪛 actionlint (1.7.10)
.github/workflows/test.yml

14-14: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


17-17: the runner of "actions/setup-python@v4" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🪛 Ruff (0.14.10)
serpent/core.py

273-273: Unused method argument: node

(ARG002)


288-288: Unused method argument: node

(ARG002)

serpent_scripts/serpent_graphviz.py

271-271: Unused method argument: node

(ARG002)


286-286: Unused method argument: node

(ARG002)

serpent/app.py

210-210: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (8)
.gitignore (1)

1-5: LGTM.

Standard .gitignore configuration appropriate for a Python project with Poetry/build artifacts.

.streamlit/config.toml (1)

1-7: LGTM!

The Streamlit theme configuration looks correct with valid settings for base theme, colors, and font.

pyproject.toml (1)

1-34: LGTM!

The Poetry configuration is well-structured with appropriate dependency specifications, dev tooling, and CLI entry point. The pylint message controls are reasonable for this project type.

serpent_scripts/serpent_graphviz.py (3)

163-165: Empty if body handling may produce unexpected edges.

When node.body is empty (which is syntactically invalid in Python but could occur in partial AST manipulation), true_end_nodes is set to [decision_node_id]. This means the "True" edge won't be labeled since next_edge_label was set but never consumed by a new_node call. The behavior is acceptable since empty bodies are rare, but worth noting.


271-298: Unused node parameter is intentional for AST visitor pattern.

The static analysis flags node as unused in visit_Break and visit_Continue. This is expected since the AST visitor pattern requires this signature, and break/continue statements carry no additional data. The current implementation is correct.


300-308: LGTM!

The visit_Return correctly terminates flow by clearing last_nodes, preventing subsequent statements from connecting to it.

serpent/__init__.py (1)

1-5: LGTM!

Clean package initialization that exposes the primary public API. Consider also exporting PythonFlowchartGV in __all__ if users need direct access to the visitor class for customization.

tests/test_flowchart.py (1)

8-21: LGTM!

Good basic test for linear function flow. Consider adding a test for functions with parameters to verify they're displayed correctly.

@Asifdotexe Asifdotexe changed the title 27 update the flowchart capability 27 Refactor project structure for Poetry and improve flowchart capability Jan 11, 2026
Copy link
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: 0

🧹 Nitpick comments (2)
serpent/app.py (2)

174-179: Consider removing redundant AST parsing.

The code calls ast.parse(code) at line 176 for validation, but generate_graphviz_flowchart also parses the code internally. For valid code, this results in parsing twice.

You could either:

  1. Remove the validation here and let generate_graphviz_flowchart handle syntax errors (it returns an error graph), or
  2. Modify generate_graphviz_flowchart to accept a pre-parsed AST tree

The current approach is functional but slightly inefficient.


181-185: Consider extracting filename sanitization to a helper function.

The sanitization logic works correctly but could be more maintainable as a separate helper function, especially if you need to reuse this logic elsewhere.

♻️ Optional refactor
def sanitize_filename(title: str, max_length: int = 50, fallback: str = "flowchart") -> str:
    """Sanitize a title for use as a filename."""
    safe = re.sub(r"[^a-z0-9_\-]", "", title.lower().replace(" ", "_"))
    safe = re.sub(r"_+", "_", safe).strip("_")
    return safe[:max_length] or fallback

Then use it:

safe_title = sanitize_filename(chart_title)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fda3153 and f0f35fb.

📒 Files selected for processing (3)
  • .pre-commit-config.yaml
  • serpent/app.py
  • serpent/core.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • serpent/core.py
  • .pre-commit-config.yaml
🧰 Additional context used
🧬 Code graph analysis (1)
serpent/app.py (2)
serpent/core.py (1)
  • generate_graphviz_flowchart (364-392)
serpent_scripts/serpent_graphviz.py (1)
  • generate_graphviz_flowchart (362-390)
🔇 Additional comments (9)
serpent/app.py (9)

1-22: LGTM!

The imports are well-organized and the module docstring clearly describes the app's purpose.


24-32: Good fallback handling for missing assets.

The graceful fallback to None when assets are missing is a solid choice for package distribution scenarios.


34-53: LGTM!

The page configuration and custom styling are appropriate. The empathic comments add a nice touch.


55-66: LGTM!

The placeholder code is engaging and demonstrates the tool's capabilities effectively.


76-112: LGTM!

The UI setup is user-friendly with good fallback handling and helpful guidance for users.


117-149: LGTM!

The input column is well-structured with clear labels and helpful tooltips.


187-211: LGTM!

The conditional download options based on Graphviz availability provide excellent UX with clear messaging and helpful fallbacks.


213-221: LGTM!

The exception handling is well-structured with appropriate logging and user-friendly error messages.


223-233: LGTM!

The entry point follows standard Python conventions and the footer encourages community engagement.

Copy link
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: 0

🧹 Nitpick comments (1)
tests/test_app_smoke.py (1)

11-15: Add bounds checking for caption assertion.

The test directly indexes at.caption[0] without verifying that a caption element exists. If the app doesn't render a caption (e.g., based on the "conditional banner presence" mentioned in line 13), this will raise an IndexError rather than a more informative AssertionError.

Additionally, the hardcoded string comparison is brittle and will break with any minor text changes to the app.

♻️ Suggested improvements for robustness
 def test_app_startup():
     """
     Smoke test to verify the app starts up without errors.
     """
     at = AppTest.from_file(app.__file__).run()
     
-    # Check if the caption is correct (title is conditional on banner presence)
-    assert at.caption[0].value == "Turn your Python functions into clear, standard flowcharts in a few clicks."
+    # Check if the caption is correct (title is conditional on banner presence)
+    assert len(at.caption) > 0, "Expected at least one caption element"
+    assert at.caption[0].value == "Turn your Python functions into clear, standard flowcharts in a few clicks."
     assert not at.exception
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f0f35fb and 460d799.

📒 Files selected for processing (1)
  • tests/test_app_smoke.py

@Asifdotexe Asifdotexe merged commit b711c68 into main Jan 11, 2026
2 checks passed
@Asifdotexe Asifdotexe deleted the 27-update-the-flowchart-capability branch January 11, 2026 08:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

update the flowchart capability

2 participants