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
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.11", "3.12", "3.13", "3.14"]
python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"]

defaults:
run:
Expand Down
7 changes: 7 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@

## Bug fixes

* Avoid pandas 3.1 deprecation warnings when building a graph from GDS

## Improvements

* Support Python 3.15
* Support the GDS client 2.3

## Other changes

* Drop support for Python 3.10
* On Python 3.15, the `pandas` extra installs pandas 3 (pandas 2 has no 3.15 wheels)
* The `snowflake` extra is not available on Python 3.15 until `snowflake-connector-python` ships 3.15 wheels
16 changes: 12 additions & 4 deletions python-wrapper/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ classifiers = [
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Topic :: Database :: Front-Ends",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Visualization",
Expand All @@ -41,10 +42,17 @@ requires-python = ">=3.11"


[project.optional-dependencies]
pandas = ["pandas>=2, <3", "pandas-stubs>=2, <3"]
# pandas 2 has no Python 3.15 wheels, so 3.15+ uses pandas 3 (which needs >=3.11).
pandas = [
"pandas>=2, <3; python_version < '3.15'",
"pandas>=3, <4; python_version >= '3.15'",
"pandas-stubs>=2, <3; python_version < '3.15'",
"pandas-stubs>=3, <4; python_version >= '3.15'",
]
gds = ["graphdatascience>=1.22, <3"]
neo4j = ["neo4j"]
snowflake = ["snowflake-snowpark-python<2,>=1"]
# snowflake-connector-python has no 3.15 wheels yet, so the extra is unavailable there.
snowflake = ["snowflake-snowpark-python<2,>=1; python_version < '3.15'"]
# The Streamlit integration (neo4j_viz.streamlit) uses the Components v2 API,
# available from streamlit 1.58.
streamlit = ["streamlit<2,>=1.58"]
Expand All @@ -61,7 +69,7 @@ dev = [
"pytest-mock==3.17.0",
"playwright==1.64.1",
"nbconvert==7.17.2",
"streamlit==1.65.0",
"streamlit==1.66.1",
"matplotlib>=3.9.4",
"jupyterlab>=4.5.7",
"marimo",
Expand All @@ -83,7 +91,7 @@ notebook = [
"ipywidgets>=8.0.0",
"palettable>=3.3.3",
"matplotlib>=3.9.4",
"snowflake-snowpark-python==1.55.3",
"snowflake-snowpark-python==1.55.3; python_version < '3.15'",
"requests",
"python-dotenv"
]
Expand Down
16 changes: 9 additions & 7 deletions python-wrapper/src/neo4j_viz/gds.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,7 @@ def from_gds(
gds, G_fetched, node_properties_by_label, node_properties_by_label.keys(), db_node_properties
)
if property_name is not None:
for df in node_dfs.values():
df.drop(columns=[property_name], inplace=True)
node_dfs = {lbl: df.drop(columns=[property_name]) for lbl, df in node_dfs.items()}

rel_dfs = _fetch_rel_dfs(gds, G_fetched)
finally:
Expand All @@ -163,16 +162,19 @@ def from_gds(
elif property_name is not None:
_catalog(gds).node_properties.drop(G_fetched, node_properties=[property_name])

for df in node_dfs.values():
if property_name is not None and property_name in df.columns:
df.drop(columns=[property_name], inplace=True)
if property_name is not None:
node_dfs = {
lbl: df.drop(columns=[property_name]) if property_name in df.columns else df for lbl, df in node_dfs.items()
}

node_props_df = pd.concat(node_dfs.values(), ignore_index=True, axis=0).drop_duplicates(subset=["nodeId"])

for lbl, df in node_dfs.items():
for lbl in node_dfs:
df = node_dfs[lbl]
if "labels" in all_gds_properties:
df.rename(columns={"labels": "__labels"}, inplace=True)
df = df.rename(columns={"labels": "__labels"})
df["labels"] = lbl
node_dfs[lbl] = df

node_labels_df = pd.concat([df[["nodeId", "labels"]] for df in node_dfs.values()], ignore_index=True, axis=0)
node_labels_df = node_labels_df.groupby("nodeId").agg({"labels": list})
Expand Down
4 changes: 2 additions & 2 deletions python-wrapper/src/neo4j_viz/pandas.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def _parse_nodes(node_dfs: DFS_TYPE, dropna: bool = False) -> list[Node]:
for node_df in node_dfs_iter:
for _, row in node_df.iterrows():
if dropna:
row = row.dropna(inplace=False)
row = row.dropna()
mandatory_fields = {}
properties = {}
for key, value in row.to_dict().items():
Expand Down Expand Up @@ -102,7 +102,7 @@ def _parse_relationships(rel_dfs: DFS_TYPE, dropna: bool = False) -> list[Relati
for rel_df in rel_dfs_iter:
for _, row in rel_df.iterrows():
if dropna:
row = row.dropna(inplace=False)
row = row.dropna()
mandatory_fields = {}
properties = {}
for key, value in row.to_dict().items():
Expand Down
Loading
Loading