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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion graphify/cluster.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
"""Community detection on NetworkX graphs. Uses Leiden (graspologic) if available, falls back to Louvain (networkx). Splits oversized communities. Returns cohesion scores."""
from __future__ import annotations
import contextlib
import io
import os
import sys
import networkx as nx


def _suppress_output():
"""Context manager to suppress stdout/stderr during library calls.

graspologic's leiden() emits ANSI escape sequences (progress bars,
colored warnings) that corrupt PowerShell 5.1's scroll buffer on
Windows (see issue #19). Redirecting stdout/stderr to devnull during
the call prevents this without losing any graphify output.
"""
return contextlib.redirect_stdout(io.StringIO())


def _partition(G: nx.Graph) -> dict[str, int]:
"""Run community detection. Returns {node_id: community_id}.

Tries Leiden (graspologic) first — best quality.
Falls back to Louvain (built into networkx) if graspologic is not installed.

Output from graspologic is suppressed to prevent ANSI escape codes
from corrupting terminal scroll buffers on Windows PowerShell 5.1.
"""
try:
from graspologic.partition import leiden
return leiden(G)
# Suppress graspologic output to prevent ANSI escape codes from
# corrupting PowerShell 5.1 scroll buffer (issue #19)
old_stderr = sys.stderr
try:
sys.stderr = io.StringIO()
with _suppress_output():
result = leiden(G)
finally:
sys.stderr = old_stderr
return result
except ImportError:
pass

Expand Down
13 changes: 13 additions & 0 deletions graphify/skill-windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,19 @@ graphify claude uninstall # remove the section

---

## Troubleshooting

### PowerShell 5.1: Vertical scrolling stops working

If vertical scrolling breaks in PowerShell after running graphify, this is caused by ANSI escape sequences from the `graspologic` library. Graphify v0.3.10+ suppresses this output, but if you still see the issue:

1. **Upgrade graphify**: `pip install --upgrade graphifyy`
2. **Use Windows Terminal** instead of the legacy PowerShell console — Windows Terminal handles ANSI codes correctly
3. **Reset your terminal**: close and reopen PowerShell
4. **Skip graspologic**: uninstall it (`pip uninstall graspologic`) and graphify will fall back to NetworkX's built-in Louvain algorithm, which produces no ANSI output

---

## Honesty Rules

- Never invent an edge. If unsure, use AMBIGUOUS.
Expand Down
24 changes: 24 additions & 0 deletions tests/test_cluster.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import sys
import networkx as nx
from pathlib import Path
from graphify.build import build_from_json
Expand Down Expand Up @@ -50,3 +51,26 @@ def test_score_all_keys_match_communities():
communities = cluster(G)
scores = score_all(G, communities)
assert set(scores.keys()) == set(communities.keys())


def test_cluster_does_not_write_to_stdout(capsys):
"""Clustering should not emit ANSI escape codes or other output.

graspologic's leiden() can emit ANSI escape sequences that break
PowerShell 5.1's scroll buffer on Windows (issue #19). The output
suppression in _partition() should prevent any output from leaking.
"""
G = make_graph()
cluster(G)
captured = capsys.readouterr()
assert captured.out == "", f"cluster() wrote to stdout: {captured.out!r}"


def test_cluster_does_not_write_to_stderr(capsys):
"""Same as above but for stderr — ANSI codes can go to either stream."""
G = make_graph()
cluster(G)
captured = capsys.readouterr()
# Allow logging output (starts with [graphify]) but no raw ANSI codes
for line in captured.err.splitlines():
assert "\x1b" not in line, f"cluster() wrote ANSI to stderr: {line!r}"