Skip to content

Commit cdd743a

Browse files
lmeyerovclaude
andauthored
fix(imports): replace relative imports with absolute imports in GFQL modules (#681)
* fix(imports): replace relative imports with absolute imports in GFQL modules - Replace all '..' relative imports with absolute 'graphistry.' imports - Fixes pip install issues caused by relative imports - Add lint check to prevent future relative imports The relative imports were breaking module resolution when installed via pip. All imports now use absolute paths from the graphistry package root. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * docs(changelog): add v0.38.3 entry for import fixes - Document fix for relative imports in GFQL modules - Note addition of lint check for relative imports 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * fix(models): add missing __init__.py files for gfql modules - Add empty __init__.py files to graphistry.models.gfql directories - Fixes ModuleNotFoundError when importing after pip install - Add docker test scripts to verify pip install works - Add CI job to test pip install across Python versions The missing __init__.py files prevented Python from recognizing the gfql directories as packages, causing import errors after pip install. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * chore: remove temporary minimal test script * refactor(ci): move pip install test to step in test-minimal-python - Remove separate test-pip-install job - Add Docker pip install test as step after typecheck - Ensures packaging is tested after lint/typecheck pass - Reduces CI complexity while maintaining test coverage 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * docs(changelog): update PR reference and details * style: fix import order to follow project conventions - Typing imports first - Then stdlib/third-party imports - One empty line - Then package global imports (from graphistry...) - Then local imports (from .) - Two empty lines before class/function definitions 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * style: alphabetize imports within sections - Sort imports alphabetically within each import section - Maintains proper import order: typing, stdlib/third-party, package, local - Improves code consistency and readability 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent f6cb340 commit cdd743a

File tree

14 files changed

+102
-44
lines changed

14 files changed

+102
-44
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ jobs:
6060
source pygraphistry/bin/activate
6161
./bin/typecheck.sh
6262
63+
- name: Test pip install (Docker)
64+
env:
65+
PYTHON_VERSION: ${{ matrix.python-version }}
66+
run: |
67+
./docker/test-pip-install.sh
68+
6369
- name: Minimal tests
6470
run: |
6571
source pygraphistry/bin/activate

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
77

88
## Dev
99

10+
## [0.38.3 - 2025-06-24]
11+
12+
### Fixed
13+
* Fix relative imports in GFQL modules that broke pip install (#681)
14+
* Replace all `..` relative imports with absolute `graphistry.` imports
15+
* Add missing `__init__.py` files in `graphistry.models.gfql` subdirectories
16+
* Add lint check in `bin/lint.sh` to prevent future relative imports
17+
* Add Docker-based pip install test to CI pipeline
18+
1019
## [0.38.2 - 2025-06-24]
1120

1221
### Added

bin/lint.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,11 @@ flake8 \
2323
--max-complexity=10 \
2424
--max-line-length=127 \
2525
--statistics
26+
27+
# Check for relative imports with '..' using flake8-quotes or custom regex
28+
# This will fail if any relative imports with .. are found
29+
echo "Checking for relative imports with '..' ..."
30+
if grep -r "from \.\." graphistry --include="*.py" --exclude-dir="__pycache__"; then
31+
echo "ERROR: Found relative imports with '..'. Use absolute imports instead."
32+
exit 1
33+
fi

docker/test-pip-install.sh

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/bin/bash
2+
set -ex
3+
4+
# Test PyGraphistry pip install in a clean environment
5+
# This verifies that all imports work correctly after pip install
6+
7+
echo "=== Testing PyGraphistry pip install ==="
8+
9+
PYTHON_VERSION=${PYTHON_VERSION:-3.11}
10+
11+
# Run in Docker container with inline Python test
12+
docker run --rm \
13+
-v "$(pwd)":/workspace \
14+
python:${PYTHON_VERSION}-slim \
15+
bash -c "cd /workspace && pip install . && python -c '
16+
import sys
17+
import traceback
18+
19+
try:
20+
print(\"Testing: import graphistry\")
21+
import graphistry
22+
print(\"✓ Success: import graphistry\")
23+
24+
print(\"\\nTesting: from graphistry.compute.predicates.types import NormalizedIsInElement\")
25+
from graphistry.compute.predicates.types import NormalizedIsInElement
26+
print(\"✓ Success: predicates.types import\")
27+
28+
print(\"\\nTesting: from graphistry.models.gfql.types.temporal import DateTimeWire\")
29+
from graphistry.models.gfql.types.temporal import DateTimeWire
30+
print(\"✓ Success: gfql.types.temporal import\")
31+
32+
print(\"\\nTesting: from graphistry.compute.ast_temporal import TemporalValue\")
33+
from graphistry.compute.ast_temporal import TemporalValue
34+
print(\"✓ Success: ast_temporal import\")
35+
36+
print(\"\\nAll imports successful!\")
37+
sys.exit(0)
38+
except Exception as e:
39+
print(f\"\\n✗ Error: {type(e).__name__}: {e}\")
40+
traceback.print_exc()
41+
sys.exit(1)
42+
'"

graphistry/compute/ast_temporal.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1+
from typing import Any, Dict, Union
12
from abc import ABC, abstractmethod
2-
from datetime import datetime, date, time
3-
from typing import Dict, Any, Union, TYPE_CHECKING
4-
import pandas as pd
3+
from datetime import date, datetime, time
54
from dateutil import parser as date_parser # type: ignore[import]
5+
import pandas as pd
66
import pytz # type: ignore[import]
77

8+
from graphistry.models.gfql.types.temporal import DateTimeWire, DateWire, TimeWire, TemporalWire
89
from graphistry.utils.json import JSONVal
910

10-
if TYPE_CHECKING:
11-
from ..models.gfql.types.temporal import DateTimeWire, DateWire, TimeWire, TemporalWire
12-
1311

1412
class TemporalValue(ABC):
1513
"""Base class for temporal values with tagging support"""
1614

1715
@abstractmethod
18-
def to_json(self) -> 'TemporalWire':
16+
def to_json(self) -> TemporalWire:
1917
"""Serialize to JSON-compatible dictionary"""
2018
pass
2119

@@ -64,9 +62,8 @@ def _parse_iso8601(self, value: str, timezone: str) -> pd.Timestamp:
6462

6563
return pd.Timestamp(dt)
6664

67-
def to_json(self) -> 'DateTimeWire':
65+
def to_json(self) -> DateTimeWire:
6866
"""Return dict for tagged temporal value"""
69-
from ..models.gfql.types.temporal import DateTimeWire
7067
result: DateTimeWire = {
7168
"type": "datetime",
7269
"value": self.value,
@@ -94,9 +91,8 @@ def _parse_date(self, value: str) -> date:
9491
"""Parse date string in ISO format (YYYY-MM-DD)"""
9592
return date_parser.isoparse(value).date()
9693

97-
def to_json(self) -> 'DateWire':
94+
def to_json(self) -> DateWire:
9895
"""Return dict for tagged temporal value"""
99-
from ..models.gfql.types.temporal import DateWire
10096
result: DateWire = {
10197
"type": "date",
10298
"value": self.value
@@ -130,9 +126,8 @@ def _parse_time(self, value: str) -> time:
130126
# Extract time from full datetime
131127
return date_parser.isoparse(value).time()
132128

133-
def to_json(self) -> 'TimeWire':
129+
def to_json(self) -> TimeWire:
134130
"""Return dict for tagged temporal value"""
135-
from ..models.gfql.types.temporal import TimeWire
136131
result: TimeWire = {
137132
"type": "time",
138133
"value": self.value

graphistry/compute/predicates/comparison.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
from typing import Union, TYPE_CHECKING, Literal, Dict, cast, overload
2-
import pandas as pd
1+
from typing import Dict, Literal, TYPE_CHECKING, Union, cast, overload
2+
from datetime import date, datetime, time
33
import numpy as np
4-
from datetime import datetime, date, time
4+
import pandas as pd
55

6-
from .ASTPredicate import ASTPredicate
7-
from ..ast_temporal import TemporalValue, DateTimeValue, DateValue, TimeValue
8-
from ...models.gfql.coercions.temporal import to_ast
9-
from ...models.gfql.types.guards import is_native_numeric, is_any_temporal, is_string
10-
from graphistry.models.gfql.types.predicates import ComparisonInput, BetweenBoundInput
11-
from ...utils.json import JSONVal
6+
from graphistry.compute.ast_temporal import DateTimeValue, DateValue, TemporalValue, TimeValue
127
from graphistry.compute.typing import SeriesT
8+
from graphistry.models.gfql.coercions.temporal import to_ast
9+
from graphistry.models.gfql.types.guards import is_any_temporal, is_native_numeric, is_string
10+
from graphistry.models.gfql.types.predicates import BetweenBoundInput, ComparisonInput
11+
from graphistry.utils.json import JSONVal
12+
from .ASTPredicate import ASTPredicate
1313

1414
if TYPE_CHECKING:
1515
# Note: pd.Series[T] syntax not supported in Python 3.8

graphistry/compute/predicates/is_in.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
from typing import Any, List, Union
2-
from .types import NormalizedIsInElement, NormalizedScalar, NormalizedNumeric
3-
import pandas as pd
2+
from datetime import date, datetime, time
43
import numpy as np
5-
from datetime import datetime, date, time
4+
import pandas as pd
65

6+
from graphistry.compute.typing import SeriesT
7+
from graphistry.models.gfql.coercions.temporal import to_native
8+
from graphistry.models.gfql.types.guards import is_any_temporal, is_basic_scalar
9+
from graphistry.models.gfql.types.predicates import IsInElementInput
10+
from graphistry.models.gfql.types.temporal import DateTimeWire, DateWire, TemporalWire, TimeWire
711
from graphistry.utils.json import assert_json_serializable
812
from .ASTPredicate import ASTPredicate
9-
# Temporal value classes imported for type checking only
10-
from ...models.gfql.coercions.temporal import to_native
11-
from ...models.gfql.types.guards import is_basic_scalar, is_any_temporal
12-
from graphistry.models.gfql.types.predicates import IsInElementInput
13-
from graphistry.models.gfql.types.temporal import TemporalWire, DateTimeWire, DateWire, TimeWire
14-
from graphistry.compute.typing import SeriesT
13+
from .types import NormalizedIsInElement, NormalizedNumeric, NormalizedScalar
1514

1615

1716
class IsIn(ASTPredicate):

graphistry/compute/predicates/types.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
"""Type definitions for predicates"""
2-
from typing import Union, Literal
3-
import pandas as pd
2+
from typing import Literal, Union
3+
from datetime import date, datetime, time
44
import numpy as np
5-
from datetime import datetime, date, time
5+
import pandas as pd
66

7-
# Import temporal wire types
8-
from ...models.gfql.types.temporal import DateTimeWire, DateWire, TimeWire
7+
from graphistry.models.gfql.types.temporal import DateTimeWire, DateWire, TimeWire
98

109
# Normalized types after processing inputs
1110
NormalizedNumeric = Union[int, float, np.number]

graphistry/models/gfql/__init__.py

Whitespace-only changes.

graphistry/models/gfql/coercions/__init__.py

Whitespace-only changes.

graphistry/models/gfql/coercions/numeric.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Union
99
import numpy as np
1010

11-
from ..types.numeric import NativeNumeric
11+
from graphistry.models.gfql.types.numeric import NativeNumeric
1212

1313

1414
def to_native(val: NativeNumeric) -> NativeNumeric:

graphistry/models/gfql/coercions/temporal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
from datetime import datetime, date, time
99
import pandas as pd
1010

11-
from ....compute.ast_temporal import (
11+
from graphistry.compute.ast_temporal import (
1212
TemporalValue, DateTimeValue, DateValue, TimeValue
1313
)
14-
from ..types.temporal import NativeTemporal, TemporalWire
14+
from graphistry.models.gfql.types.temporal import NativeTemporal, TemporalWire
1515

1616

1717
# ============= To AST Transforms =============

graphistry/models/gfql/types/__init__.py

Whitespace-only changes.

graphistry/models/gfql/types/guards.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
"""
66

77
from typing import Any, TYPE_CHECKING, Union
8+
from datetime import date, datetime, time
9+
import numpy as np
10+
import pandas as pd
811

912
# Python 3.10+ has TypeGuard in typing module, older versions need typing_extensions
1013
if TYPE_CHECKING:
@@ -13,13 +16,10 @@
1316
from typing import TypeGuard
1417
else:
1518
from typing_extensions import TypeGuard
16-
from datetime import datetime, date, time
17-
import pandas as pd
18-
import numpy as np
1919

20-
from ....compute.ast_temporal import TemporalValue
21-
from .temporal import NativeTemporal, TemporalWire
20+
from graphistry.compute.ast_temporal import TemporalValue
2221
from .numeric import NativeNumeric
22+
from .temporal import NativeTemporal, TemporalWire
2323

2424

2525
# ============= Temporal Detection =============

0 commit comments

Comments
 (0)