Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
f8c01d0
feat(gfql): add core AST nodes for GFQL Programs
lmeyerov Jul 19, 2025
465981c
feat(gfql): implement DAG execution with dependency resolution
lmeyerov Jul 19, 2025
9bf05bb
feat(gfql): add unified API with deprecation migration
lmeyerov Jul 19, 2025
7f6cc9e
fix(gfql): improve JSON serialization error messages
lmeyerov Jul 19, 2025
e007704
feat(gfql): add schema validation and remote graph support
lmeyerov Jul 19, 2025
3f0765a
feat(gfql): add user-friendly aliases for AST classes
lmeyerov Jul 20, 2025
73cf7ed
fix: clean up lint, type, and test issues after rebase
lmeyerov Jul 23, 2025
184e6e4
fix(lint): fix flake8 and mypy type errors in PR #706
lmeyerov Jul 24, 2025
9585397
fix(ci): trigger CI re-run to verify lint fixes
lmeyerov Jul 24, 2025
e8ef1bb
fix(lint): add whitespace around arithmetic operator in f-string
lmeyerov Jul 24, 2025
b52faa5
fix(test): correct import path for validate_schema module
lmeyerov Jul 24, 2025
554e558
refactor(ast): rename ASTQueryDAG to ASTLet for clarity
lmeyerov Jul 24, 2025
d64cf6a
refactor(compute): update imports and references to use ASTLet
lmeyerov Jul 24, 2025
4296078
test: rename test_querydag.py to test_let.py
lmeyerov Jul 24, 2025
dbf4df6
test: update all tests to use ASTLet instead of ASTQueryDAG
lmeyerov Jul 24, 2025
967f4a7
test: fix numpy.bool_ comparison in chain_dag tests
lmeyerov Jul 24, 2025
e27f16e
chore: ignore test_env directory
lmeyerov Jul 24, 2025
34f1abe
fix(gfql): update ASTLet.__call__ error message to reflect implementa…
lmeyerov Jul 24, 2025
36a7010
fix(gfql): implement ASTLet.__call__ to proxy to chain_dag_impl
lmeyerov Jul 24, 2025
4901f9e
fix(lint): fix critical import and lint issues in PR #706
lmeyerov Jul 24, 2025
e15a6d6
fix(types): fix mypy type errors in ast.py, chain_dag.py, and validat…
lmeyerov Jul 24, 2025
15de494
fix(lint): add newline at end of __init__.py
lmeyerov Jul 24, 2025
a9b8ff0
fix(tests): correct import paths for is_in predicate in test files
lmeyerov Jul 25, 2025
ccdc867
refactor(gfql): rename ASTChainRef to ASTRef for cleaner terminology
lmeyerov Jul 26, 2025
97ea6b0
feat(gfql): implement ASTCall with safelist validation
lmeyerov Jul 19, 2025
9f6d44e
feat(gfql): add 'call' alias for ASTCall
lmeyerov Jul 20, 2025
bcb5659
feat(gfql): add schema validation for Call operations
lmeyerov Jul 19, 2025
34e321a
feat(gfql): add comprehensive docstrings and GPU tests for Call opera…
lmeyerov Jul 19, 2025
41c503e
refactor: move Call operation files to compute/gfql/
lmeyerov Jul 21, 2025
f9641de
fix: add missing newline to test_call_operations_gpu.py to fix W292 l…
lmeyerov Jul 21, 2025
7a550fa
fix: revert gfql subdirectory refactoring that caused import conflicts
lmeyerov Jul 24, 2025
1a09bf2
fix(rebase): update ASTQueryDAG references to ASTLet after rebase
lmeyerov Jul 24, 2025
e7daf6f
fix(lint): add 'call' to __all__ exports after rebase
lmeyerov Jul 25, 2025
2a57411
fix: update validate_schema import paths in test files
lmeyerov Jul 25, 2025
cea5069
fix(test): update ASTChainRef import to ASTRef in test_call_operation…
lmeyerov Jul 26, 2025
7afef47
Merge remote-tracking branch 'origin/feature/gfql-pr1-ast-nodes' into…
lmeyerov Jul 28, 2025
c5ee4a6
Merge remote-tracking branch 'origin/feature/gfql-pr1-3-call-operatio…
lmeyerov Jul 28, 2025
3e310fb
feat(gfql): add PR #709a code changes - new directory structure and i…
lmeyerov Jul 28, 2025
3b394a2
refactor(gfql): remove old call_executor/safelist files moved to gfql…
lmeyerov Jul 28, 2025
27519d3
refactor(compute): rename chain_dag to chain_let throughout codebase
lmeyerov Jul 28, 2025
a896e04
feat(compute): update AST validation to use _validate_fields pattern
lmeyerov Jul 28, 2025
05c8abc
feat(gfql): Add allow_fragments parameter with safe default False
lmeyerov Jul 28, 2025
c37d16e
fix(gfql): Fix import paths for call_executor and EngineAbstract usage
lmeyerov Jul 28, 2025
1afac76
fix(gfql): Fix remaining import paths for call_executor and call_safe…
lmeyerov Jul 28, 2025
5ef4d97
fix(gfql): Export call_executor and call_safelist from gfql module
lmeyerov Jul 28, 2025
928acf1
fix(test): Fix undefined name errors in test_call_operations.py
lmeyerov Jul 28, 2025
e3762d7
fix(test): Update test_gfql_with_single_ast_object to expect allow_fr…
lmeyerov Jul 28, 2025
2e32bc3
fix(tests): Update validation tests to expect GFQLTypeError instead o…
lmeyerov Jul 28, 2025
d782864
fix(validation): Use correct error code E201 instead of non-existent …
lmeyerov Jul 28, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,4 @@ docs/source/demos
# AI assistant working directories
AI_PROGRESS/
PLAN.md
test_env/
41 changes: 34 additions & 7 deletions graphistry/compute/ASTSerializable.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC
from typing import Dict, List, Optional, TYPE_CHECKING
from typing import Dict, List, Optional, Sequence, TYPE_CHECKING

from graphistry.utils.json import JSONVal, serialize_to_json_val

Expand Down Expand Up @@ -34,7 +34,14 @@ def validate(self, collect_all: bool = False) -> Optional[List['GFQLValidationEr
self._validate_fields()
# Validate children
for child in self._get_child_validators():
child.validate(collect_all=False)
if hasattr(child, 'validate'):
# Check if validate method accepts collect_all parameter
import inspect
sig = inspect.signature(child.validate)
if 'collect_all' in sig.parameters:
child.validate(collect_all=False)
else:
child.validate()
return None

# Collect all errors mode
Expand All @@ -54,9 +61,29 @@ def validate(self, collect_all: bool = False) -> Optional[List['GFQLValidationEr

# Collect child validation errors
for child in self._get_child_validators():
child_errors = child.validate(collect_all=True)
if child_errors:
errors.extend(child_errors)
if hasattr(child, 'validate'):
# Check if validate method accepts collect_all parameter
import inspect
sig = inspect.signature(child.validate)
if 'collect_all' in sig.parameters:
child_errors = child.validate(collect_all=True)
if child_errors:
errors.extend(child_errors)
else:
# Old-style validate without collect_all
try:
child.validate()
except Exception as e:
from graphistry.compute.exceptions import GFQLValidationError
if isinstance(e, GFQLValidationError):
errors.append(e)
else:
from graphistry.compute.exceptions import ErrorCode
errors.append(GFQLValidationError(
ErrorCode.E201,
f"Validation error in child: {str(e)}",
value=str(child)
))

return errors

Expand All @@ -68,11 +95,11 @@ def _validate_fields(self) -> None:
"""
pass

def _get_child_validators(self) -> List['ASTSerializable']:
def _get_child_validators(self) -> Sequence['ASTSerializable']:
"""Override in subclasses to return child AST nodes that need validation.

Returns:
List of child AST nodes to validate
Sequence of child AST nodes to validate
"""
return []

Expand Down
75 changes: 70 additions & 5 deletions graphistry/compute/ComputeMixin.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import numpy as np, pandas as pd
import numpy as np
import pandas as pd
from typing import Any, List, Union
from inspect import getmodule

from graphistry.Engine import Engine, EngineAbstract
from graphistry.Plottable import Plottable
from graphistry.util import setup_logger
from .chain import chain as chain_base
from .chain_remote import chain_remote as chain_remote_base, chain_remote_shape as chain_remote_shape_base
from .gfql_unified import gfql as gfql_base
from .chain_remote import (
chain_remote as chain_remote_base,
chain_remote_shape as chain_remote_shape_base
)
from .python_remote import (
python_remote_g as python_remote_g_base,
python_remote_table as python_remote_table_base,
Expand Down Expand Up @@ -460,16 +465,76 @@ def filter_edges_by_dict(self, *args, **kwargs):
filter_edges_by_dict.__doc__ = filter_edges_by_dict_base.__doc__

def chain(self, *args, **kwargs):
"""
.. deprecated:: 2.XX.X
Use :meth:`gfql` instead for a unified API that supports both chains and DAGs.
"""
import warnings
warnings.warn(
"chain() is deprecated. Use gfql() instead for a unified API.",
DeprecationWarning,
stacklevel=2
)
return chain_base(self, *args, **kwargs)
chain.__doc__ = chain_base.__doc__
# Preserve original docstring after deprecation notice
chain.__doc__ = (chain.__doc__ or "") + "\n\n" + (chain_base.__doc__ or "")

# chain_let removed from public API - use gfql() instead
# (chain_let_base still available internally for gfql dispatch)

def gfql(self, *args, **kwargs):
return gfql_base(self, *args, **kwargs)
gfql.__doc__ = gfql_base.__doc__

def chain_remote(self, *args, **kwargs) -> Plottable:
"""
.. deprecated:: 2.XX.X
Use :meth:`gfql_remote` instead for a unified API that supports both chains and DAGs.
"""
import warnings
warnings.warn(
"chain_remote() is deprecated. Use gfql_remote() instead for a unified API.",
DeprecationWarning,
stacklevel=2
)
return chain_remote_base(self, *args, **kwargs)
chain_remote.__doc__ = chain_remote_base.__doc__
# Preserve original docstring after deprecation notice
chain_remote.__doc__ = (chain_remote.__doc__ or "") + "\n\n" + (chain_remote_base.__doc__ or "")

def chain_remote_shape(self, *args, **kwargs) -> pd.DataFrame:
"""
.. deprecated:: 2.XX.X
Use :meth:`gfql_remote_shape` instead for a unified API that supports both chains and DAGs.
"""
import warnings
warnings.warn(
"chain_remote_shape() is deprecated. Use gfql_remote_shape() instead for a unified API.",
DeprecationWarning,
stacklevel=2
)
return chain_remote_shape_base(self, *args, **kwargs)
# Preserve original docstring after deprecation notice
chain_remote_shape.__doc__ = (chain_remote_shape.__doc__ or "") + "\n\n" + (chain_remote_shape_base.__doc__ or "")

def gfql_remote(self, *args, **kwargs) -> Plottable:
"""Run GFQL query remotely.

This is the remote execution version of :meth:`gfql`. It supports both simple chains
and complex DAG patterns with Let bindings.

See :meth:`chain_remote` for detailed documentation (chain_remote is deprecated).
"""
return chain_remote_base(self, *args, **kwargs)

def gfql_remote_shape(self, *args, **kwargs) -> pd.DataFrame:
"""Get shape metadata for remote GFQL query execution.

This is the remote shape version of :meth:`gfql`. Returns metadata about the
resulting graph without downloading the full data.

See :meth:`chain_remote_shape` for detailed documentation (chain_remote_shape is deprecated).
"""
return chain_remote_shape_base(self, *args, **kwargs)
chain_remote_shape.__doc__ = chain_remote_shape_base.__doc__

def python_remote_g(self, *args, **kwargs) -> Any:
return python_remote_g_base(self, *args, **kwargs)
Expand Down
39 changes: 38 additions & 1 deletion graphistry/compute/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .ComputeMixin import ComputeMixin
from .ast import (
n, e, e_forward, e_reverse, e_undirected
n, e, e_forward, e_reverse, e_undirected,
let, remote, ref, call
)
from .chain import Chain
from .predicates.is_in import (
Expand Down Expand Up @@ -54,3 +55,39 @@
notnull, NotNull,
)
from .typing import DataFrameT

__all__ = [
# Core classes
'ComputeMixin', 'Chain',
# AST nodes
'n', 'e', 'e_forward', 'e_reverse', 'e_undirected',
'let', 'remote', 'ref', 'call',
# Predicates
'is_in', 'IsIn',
'duplicated', 'Duplicated',
'is_month_start', 'IsMonthStart',
'is_month_end', 'IsMonthEnd',
'is_quarter_start', 'IsQuarterStart',
'is_quarter_end', 'IsQuarterEnd',
'is_year_start', 'IsYearStart',
'is_year_end', 'IsYearEnd',
'is_leap_year', 'IsLeapYear',
# Temporal
'TemporalValue', 'DateTimeValue', 'DateValue', 'TimeValue',
'temporal_value_from_json',
# Comparison predicates
'gt', 'GT', 'lt', 'LT', 'ge', 'GE', 'le', 'LE',
'eq', 'EQ', 'ne', 'NE', 'between', 'Between',
'isna', 'IsNA', 'notna', 'NotNA',
# String predicates
'contains', 'Contains', 'startswith', 'Startswith',
'endswith', 'Endswith', 'match', 'Match',
'isnumeric', 'IsNumeric', 'isalpha', 'IsAlpha',
'isdigit', 'IsDigit', 'islower', 'IsLower',
'isupper', 'IsUpper', 'isspace', 'IsSpace',
'isalnum', 'IsAlnum', 'isdecimal', 'IsDecimal',
'istitle', 'IsTitle', 'isnull', 'IsNull',
'notnull', 'NotNull',
# Types
'DataFrameT'
]
Loading
Loading