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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## [Development]
<!-- Do Not Erase This Section - Used for tracking unreleased changes -->

### Infra
- **Linting**: Replace flake8 with ruff for linting (closes #466). Config in `pyproject.toml`, scripts in `bin/ruff.sh` / `bin/lint.sh`. Cleaned stale `# noqa` comments for W503/W504/E126 (codes not applicable in ruff).

## [0.50.6 - 2026-01-27]

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion DEVELOP.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ To manually build, see `docs/`.

You may need to add ignore rules:

* flake8: bin/lint.sh
* ruff: pyproject.toml (or bin/lint.sh)
* mypi: mypi.ini
* sphinx: docs/source/conf.py

Expand Down
2 changes: 1 addition & 1 deletion ai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ docker run --rm --gpus all \
### Environment Control
| Variable | Default | Purpose |
|----------|---------|---------|
| `WITH_LINT` | 1 | Run flake8 linting |
| `WITH_LINT` | 1 | Run ruff linting |
| `WITH_TYPECHECK` | 1 | Run mypy type checking |
| `WITH_BUILD` | 0 | Build documentation |
| `WITH_NEO4J` | 0 | Run Neo4j integration tests |
Expand Down
34 changes: 17 additions & 17 deletions ai/prompts/LINT_TYPES_CHECK.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Status: [IN_PROGRESS/COMPLETE/BLOCKED]

## Instructions for AI Assistant

This template guides systematic code quality checks using flake8 (linting) and mypy (type checking) for PyGraphistry.
This template guides systematic code quality checks using ruff (linting) and mypy (type checking) for PyGraphistry.

### Quick Start - Docker Commands
```bash
Expand Down Expand Up @@ -54,7 +54,7 @@ cd docker && WITH_TYPECHECK=0 WITH_BUILD=0 WITH_TEST=0 ./test-cpu-local.sh
The lint/type check process is **iterative by design**:
- **Steps 1-2**: Identify issues AND FIX THEM IMMEDIATELY
- **Step 3**: Verify and decide to iterate or finish
- **Repeat**: Continue until clean (0 flake8, 0 mypy)
- **Repeat**: Continue until clean (0 ruff, 0 mypy)
- **Step 4**: Generate final report and cleanup
- **Exit**: When clean OR blocked AFTER ATTEMPTING FIXES

Expand All @@ -75,7 +75,7 @@ The lint/type check process is **iterative by design**:

## Execution Protocol

### Step 1: Check Lint Issues with flake8
### Step 1: Check Lint Issues with ruff
**Started**: [YYYY-MM-DD HH:MM:SS]
**Command (containerized)**: `cd docker && WITH_BUILD=0 WITH_TEST=0 ./test-cpu-local.sh`
**Command (direct)**: `./bin/lint.sh` (requires local environment)
Expand Down Expand Up @@ -178,11 +178,11 @@ cd docker && WITH_BUILD=0 WITH_TEST=0 ./test-cpu-local.sh
```

**Results**:
- **Flake8 issues remaining**: [count]
- **Ruff issues remaining**: [count]
- **MyPy issues remaining**: [count]

**Decision**:
- [ ] ✅ **CLEAN** - 0 flake8 issues AND 0 mypy issues (excluding P4/P5) → Go to Step 4
- [ ] ✅ **CLEAN** - 0 ruff issues AND 0 mypy issues (excluding P4/P5) → Go to Step 4
- [ ] 🔄 **ITERATE** - P0-P3 issues remain → REPEAT Steps 1-3
- [ ] 🛑 **BLOCKED** - Cannot fix remaining P0-P3 issues → Document blockers → Go to Step 4

Expand All @@ -198,7 +198,7 @@ Examples of valid blockers:
- "Circular import that breaks when fixed"
- "Third-party library missing type stubs configured in mypy.ini"
- "Type system limitation requiring major refactor"
- "Flake8 rule conflicts with project style guide"
- "Ruff rule conflicts with project style guide"

NOT valid reasons for BLOCKED:
- "Too many errors"
Expand All @@ -222,7 +222,7 @@ Issues Fixed: [count] ([percentage]%)
Time Elapsed: [duration]

Summary by Tool:
- Flake8 issues fixed: [count]
- Ruff issues fixed: [count]
- MyPy issues fixed: [count]
- Remaining issues: [count]

Expand Down Expand Up @@ -250,20 +250,20 @@ Result: ✅ CLEAN / 🔧 IMPROVED / 🛑 BLOCKED
- **Current Status**: [count remaining]

**Per-Iteration Progress**:
| Iteration | Started | Flake8 Fixed | Flake8 Remaining | MyPy Fixed | MyPy Remaining | P4/P5 | Status |
| Iteration | Started | Ruff Fixed | Ruff Remaining | MyPy Fixed | MyPy Remaining | P4/P5 | Status |
|-----------|---------|--------------|------------------|------------|----------------|-------|--------|
| 1 | [time] | [count] | [count] | [count] | [count] | [count]| 🔄 |
| 2 | [time] | [count] | [count] | [count] | [count] | [count]| ✅ |

**Total Issues Fixed**:
- Flake8 issues fixed: [count]
- Ruff issues fixed: [count]
- MyPy errors fixed: [count]
- **P0-P3 remaining**: [count]
- **P4/P5 ignored**: [count]

## PyGraphistry-Specific Patterns

### Common Flake8 Fixes
### Common Ruff Fixes
```python
# E501: Line too long (max 127)
# Break long lines
Expand Down Expand Up @@ -311,7 +311,7 @@ df['new_col'] = values # Avoid

### Configuration Reference

**Flake8 Ignored Rules (from bin/lint.sh)**:
**Ruff Ignored Rules (from pyproject.toml [tool.ruff.lint])**:
- C901: Function complexity
- E121-E128: Indentation rules
- E201-E203: Whitespace around brackets
Expand All @@ -328,7 +328,7 @@ df['new_col'] = values # Avoid

**P4 - Nice to Have (low impact)**:
- Minor style inconsistencies that don't affect readability
- Flake8 rules explicitly ignored in bin/lint.sh (E121-E128, etc.)
- Ruff rules explicitly ignored in pyproject.toml (E121-E128, etc.)
- MyPy errors in excluded files (tests/, _version.py)
- Import errors for packages with `ignore_missing_imports = True`
- Optional type annotations that would add minimal safety benefit
Expand Down Expand Up @@ -387,7 +387,7 @@ If you have the dependencies installed locally:
./bin/mypy.sh

# For specific files
flake8 graphistry/embed_utils.py --max-line-length=127
ruff check graphistry/embed_utils.py
mypy graphistry/embed_utils.py
```

Expand Down Expand Up @@ -440,17 +440,17 @@ Started: [YYYY-MM-DD HH:MM:SS]

Fixes Applied:
1. File: [path]
- Issue: [flake8/mypy code] - [description]
- Issue: [ruff/mypy code] - [description]
- Fix: [what was changed]
- Status: ✅ Fixed

2. File: [path]
- Issue: [flake8/mypy code] - [description]
- Issue: [ruff/mypy code] - [description]
- Fix: [what was changed]
- Status: ❌ Failed - [reason]

Verification:
- Flake8: [count] remaining
- Ruff: [count] remaining
- MyPy: [count] remaining
- Next: [ITERATE/COMPLETE/BLOCKED]
```
Expand All @@ -467,7 +467,7 @@ Issues Fixed: 23 (100%)
Time Elapsed: 5 minutes 15 seconds

Summary by Tool:
- Flake8 issues fixed: 21
- Ruff issues fixed: 21
- MyPy issues fixed: 2
- Remaining issues: 0

Expand Down
50 changes: 0 additions & 50 deletions bin/flake8.sh

This file was deleted.

18 changes: 12 additions & 6 deletions bin/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,23 @@ set -ex
# Run from project root
# Non-zero exit code on fail

# Resolve flake8 command, then delegate to runner (prefer uvx, then venv)
# Resolve ruff command (prefer uvx, then bare, then python -m)
if command -v uvx >/dev/null 2>&1; then
FLAKE8_CMD="uvx flake8"
RUFF_CMD="uvx ruff"
elif command -v ruff >/dev/null 2>&1; then
RUFF_CMD="ruff"
elif command -v python >/dev/null 2>&1; then
FLAKE8_CMD="python -m flake8"
RUFF_CMD="python -m ruff"
elif command -v python3 >/dev/null 2>&1; then
RUFF_CMD="python3 -m ruff"
else
FLAKE8_CMD="flake8"
echo "ruff not found. Install ruff or set it on PATH."
exit 1
fi
FLAKE8_CMD="$FLAKE8_CMD" ./bin/flake8.sh "$@"

# Check for relative imports with '..' using flake8-quotes or custom regex
RUFF_CMD="$RUFF_CMD" ./bin/ruff.sh "$@"

# Check for relative imports with '..' using custom regex
# This will fail if any relative imports with .. are found
echo "Checking for relative imports with '..' ..."
if grep -r "from \.\." graphistry --include="*.py" --exclude-dir="__pycache__"; then
Expand Down
46 changes: 46 additions & 0 deletions bin/ruff.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/bin/bash
set -e

# Minimal resolution: env override or host ruff
RUFF_CMD_ARR=(${RUFF_CMD:-ruff})

if ! "${RUFF_CMD_ARR[@]}" version &> /dev/null; then
echo "ruff not found. Set RUFF_CMD or install ruff on PATH."
exit 1
fi

# Get the script directory and repo root
SCRIPT_DIR="$( cd "$( dirname -- "${BASH_SOURCE[0]}" )" && pwd )"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

# Change to repo root
cd "$REPO_ROOT"

# Check if specific files were passed as arguments
if [ $# -eq 0 ]; then
# No arguments, run on entire graphistry directory
TARGET="graphistry"
else
# Use provided arguments
TARGET="$@"
fi

echo "Running ruff on: $TARGET"

# Quick syntax error check (E9/F63/F7/F82)
echo "=== Running quick syntax check ==="
"${RUFF_CMD_ARR[@]}" check \
$TARGET \
--select=E9,F63,F7,F82 \
--output-format=full \
--no-fix

# Full lint check (uses config from pyproject.toml)
echo "=== Running full lint check ==="
"${RUFF_CMD_ARR[@]}" check \
$TARGET \
--output-format=full \
--statistics \
--no-fix

echo "Ruff check completed successfully!"
2 changes: 1 addition & 1 deletion docs/source/install/extended.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ For contributors and developers who wish to work on PyGraphistry itself, we reco

pip install graphistry[dev]

- **Includes**: Testing tools, documentation tools, and other development dependencies like `flake8`, `pytest`, `sphinx`, etc.
- **Includes**: Testing tools, documentation tools, and other development dependencies like `ruff`, `pytest`, `sphinx`, etc.

References
----------
Expand Down
4 changes: 2 additions & 2 deletions graphistry/PlotterBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -2885,7 +2885,7 @@ def _table_to_arrow(self, table: Any, memoize: bool = True, validate_mode: Valid
if memoize:
hashed = (
hashlib.sha256(table.hash_values().to_numpy().tobytes()).hexdigest()
+ hashlib.sha256(str(table.columns).encode('utf-8')).hexdigest() # noqa: W503
+ hashlib.sha256(str(table.columns).encode('utf-8')).hexdigest()
)
try:
if hashed in PlotterBase._cudf_hash_to_arrow:
Expand Down Expand Up @@ -3013,7 +3013,7 @@ def _make_dataset(self, edges, nodes, name, description, mode, metadata=None, me
if ('bg' in metadata) or ('fg' in metadata) or ('logo' in metadata) or ('page' in metadata):
raise ValueError('Cannot set bg/fg/logo/page in api=1; try using api=3')
if not (self._complex_encodings is None
or self._complex_encodings == { # noqa: W503
or self._complex_encodings == {
'node_encodings': {'current': {}, 'default': {} },
'edge_encodings': {'current': {}, 'default': {} }}):
raise ValueError('Cannot set complex encodings ".encode_[point/edge]_[feature]()" in api=1; try using api=3 or .bind()')
Expand Down
12 changes: 6 additions & 6 deletions graphistry/compute/hop.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,14 +640,14 @@ def resolve_label_col(requested: Optional[str], df, default_base: str) -> Option

matches_edges = concat(
[ matches_edges ]
+ ([ hop_edges_forward[[ EDGE_ID ]] ] if hop_edges_forward is not None else mt) # noqa: W503
+ ([ hop_edges_reverse[[ EDGE_ID ]] ] if hop_edges_reverse is not None else mt), # noqa: W503
+ ([ hop_edges_forward[[ EDGE_ID ]] ] if hop_edges_forward is not None else mt)
+ ([ hop_edges_reverse[[ EDGE_ID ]] ] if hop_edges_reverse is not None else mt),
ignore_index=True, sort=False).drop_duplicates(subset=[EDGE_ID])

new_node_ids = concat(
mt
+ ( [ new_node_ids_forward ] if new_node_ids_forward is not None else mt ) # noqa: W503
+ ( [ new_node_ids_reverse] if new_node_ids_reverse is not None else mt ), # noqa: W503
+ ( [ new_node_ids_forward ] if new_node_ids_forward is not None else mt )
+ ( [ new_node_ids_reverse] if new_node_ids_reverse is not None else mt ),
ignore_index=True, sort=False).drop_duplicates()

if len(new_node_ids) > 0:
Expand Down Expand Up @@ -728,10 +728,10 @@ def resolve_label_col(requested: Optional[str], df, default_base: str) -> Option
else:
matches_nodes = concat(
mt
+ ( [hop_edges_forward[[g2._source]].rename(columns={g2._source: g2._node}).drop_duplicates()] # noqa: W503
+ ( [hop_edges_forward[[g2._source]].rename(columns={g2._source: g2._node}).drop_duplicates()]
if hop_edges_forward is not None
else mt)
+ ( [hop_edges_reverse[[g2._destination]].rename(columns={g2._destination: g2._node}).drop_duplicates()] # noqa: W503
+ ( [hop_edges_reverse[[g2._destination]].rename(columns={g2._destination: g2._node}).drop_duplicates()]
if hop_edges_reverse is not None
else mt),
ignore_index=True, sort=False).drop_duplicates(subset=[g2._node])
Expand Down
12 changes: 6 additions & 6 deletions graphistry/feature_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1040,9 +1040,9 @@ def process_dirty_dataframes(
y_enc, label_encoder = encode_multi_target(y, mlb=None)
elif (
y is not None
and len(y.columns) > 0 # noqa: E126,W503
and not is_dataframe_all_numeric(y) # noqa: E126,W503
and has_skrub # noqa: E126,W503
and len(y.columns) > 0
and not is_dataframe_all_numeric(y)
and has_skrub
):
t2 = time()
logger.debug("-Fitting Targets --\n%s", y.columns)
Expand Down Expand Up @@ -1086,9 +1086,9 @@ def process_dirty_dataframes(
)
elif (
y is not None
and len(y.columns) > 0 # noqa: E126,W503
and not is_dataframe_all_numeric(y) # noqa: E126,W503
and not has_skrub # noqa: E126,W503
and len(y.columns) > 0
and not is_dataframe_all_numeric(y)
and not has_skrub
):
logger.warning("-*-*- y is not numeric and no skrub, dropping non-numeric")
y2 = y.select_dtypes(include=[np.number]) # type: ignore
Expand Down
Loading
Loading