anywidget-graph 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ .Python
6
+ build/
7
+ develop-eggs/
8
+ dist/
9
+ downloads/
10
+ eggs/
11
+ .eggs/
12
+ lib/
13
+ lib64/
14
+ parts/
15
+ sdist/
16
+ var/
17
+ wheels/
18
+ *.cl*/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+ .venv/
23
+ venv/
24
+ ENV/
25
+ .env
26
+ *.log
27
+ .coverage
28
+ htmlcov/
29
+ .pytest_cache/
30
+ .ruff_cache/
31
+ .mypy_cache/
32
+ .ty/
33
+ node_modules/
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,258 @@
1
+ Metadata-Version: 2.4
2
+ Name: anywidget-graph
3
+ Version: 0.1.0
4
+ Summary: Interactive graph visualization for Python notebooks using anywidget
5
+ Project-URL: Homepage, https://grafeo.dev/
6
+ Project-URL: Repository, https://github.com/GrafeoDB/anywidget-graph
7
+ Author-email: "S.T. Grond" <widget@grafeo.dev>
8
+ License: Apache-2.0
9
+ Keywords: anywidget,cypher,graph,jupyter,marimo,neo4j,visualization
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Framework :: Jupyter
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: Apache Software License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Scientific/Engineering :: Visualization
20
+ Requires-Python: >=3.12
21
+ Requires-Dist: anywidget>=0.9.21
22
+ Provides-Extra: dev
23
+ Requires-Dist: marimo>=0.19.7; extra == 'dev'
24
+ Requires-Dist: prek>=0.3.1; extra == 'dev'
25
+ Requires-Dist: pytest>=9.0.2; extra == 'dev'
26
+ Requires-Dist: ruff>=0.14.14; extra == 'dev'
27
+ Requires-Dist: ty>=0.0.14; extra == 'dev'
28
+ Provides-Extra: networkx
29
+ Requires-Dist: networkx>=3.0; extra == 'networkx'
30
+ Provides-Extra: pandas
31
+ Requires-Dist: pandas>=2.0; extra == 'pandas'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # anywidget-graph
35
+
36
+ Interactive graph visualization for Python notebooks.
37
+
38
+ Works with Marimo, Jupyter, VS Code, Colab, anywhere [anywidget](https://anywidget.dev/) runs.
39
+
40
+ ## Features
41
+
42
+ - **Universal** — One widget, every notebook environment
43
+ - **Backend-agnostic** — Grafeo, Neo4j, NetworkX, pandas, or raw dicts
44
+ - **Interactive** — Pan, zoom, click, expand neighbors, select paths
45
+ - **Customizable** — Colors, sizes, shapes, layouts
46
+ - **Performant** — Virtualized rendering for large graphs
47
+ - **Exportable** — PNG, SVG, JSON
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ uv add anywidget-graph
53
+ ```
54
+
55
+ ## Quick Start
56
+
57
+ ```python
58
+ from anywidget_graph import Graph
59
+
60
+ graph = Graph.from_dict({
61
+ "nodes": [
62
+ {"id": "alice", "label": "Alice", "group": "person"},
63
+ {"id": "bob", "label": "Bob", "group": "person"},
64
+ {"id": "paper", "label": "Graph Theory", "group": "document"},
65
+ ],
66
+ "edges": [
67
+ {"source": "alice", "target": "bob", "label": "knows"},
68
+ {"source": "alice", "target": "paper", "label": "authored"},
69
+ ]
70
+ })
71
+
72
+ graph
73
+ ```
74
+
75
+ ## Data Sources
76
+
77
+ ### Dictionary
78
+
79
+ ```python
80
+ from anywidget_graph import Graph
81
+
82
+ graph = Graph.from_dict({
83
+ "nodes": [{"id": "a"}, {"id": "b"}],
84
+ "edges": [{"source": "a", "target": "b"}]
85
+ })
86
+ ```
87
+
88
+ ### Grafeo
89
+
90
+ ```python
91
+ from grafeo import GrafeoDB
92
+ from anywidget_graph import Graph
93
+
94
+ db = GrafeoDB()
95
+ db.execute("INSERT (:Person {name: 'Alice'})-[:KNOWS]->(:Person {name: 'Bob'})")
96
+
97
+ result = db.execute("MATCH (a)-[r]->(b) RETURN a, r, b")
98
+ graph = Graph.from_grafeo(result)
99
+ ```
100
+
101
+ ### Neo4j
102
+
103
+ ```python
104
+ from neo4j import GraphDatabase
105
+ from anywidget_graph import Graph
106
+
107
+ driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))
108
+
109
+ with driver.session() as session:
110
+ result = session.run("MATCH (a)-[r]->(b) RETURN a, r, b LIMIT 100")
111
+ graph = Graph.from_neo4j(result)
112
+ ```
113
+
114
+ ### NetworkX
115
+
116
+ ```python
117
+ import networkx as nx
118
+ from anywidget_graph import Graph
119
+
120
+ G = nx.karate_club_graph()
121
+ graph = Graph.from_networkx(G)
122
+ ```
123
+
124
+ ### pandas
125
+
126
+ ```python
127
+ import pandas as pd
128
+ from anywidget_graph import Graph
129
+
130
+ edges = pd.DataFrame({
131
+ "source": ["alice", "alice", "bob"],
132
+ "target": ["bob", "carol", "carol"],
133
+ "weight": [1.0, 0.5, 0.8]
134
+ })
135
+
136
+ graph = Graph.from_pandas(edges)
137
+ ```
138
+
139
+ ## Interactivity
140
+
141
+ ### Events
142
+
143
+ ```python
144
+ graph = Graph.from_dict(data)
145
+
146
+ @graph.on_node_click
147
+ def handle_node(node_id, node_data):
148
+ print(f"Clicked: {node_id}")
149
+
150
+ @graph.on_edge_click
151
+ def handle_edge(edge_id, edge_data):
152
+ print(f"Edge: {edge_data['label']}")
153
+ ```
154
+
155
+ ### Selection
156
+
157
+ ```python
158
+ graph.selected_nodes # Get current selection
159
+ graph.select(["alice"]) # Select nodes
160
+ graph.clear_selection() # Clear
161
+ ```
162
+
163
+ ### Expansion
164
+
165
+ ```python
166
+ graph.expand("alice") # Show neighbors
167
+ graph.collapse("alice") # Hide neighbors
168
+ ```
169
+
170
+ ## Styling
171
+
172
+ ### By Group
173
+
174
+ ```python
175
+ graph = Graph.from_dict(
176
+ data,
177
+ node_styles={
178
+ "person": {"color": "#4CAF50", "size": 30},
179
+ "document": {"color": "#2196F3", "shape": "square"},
180
+ }
181
+ )
182
+ ```
183
+
184
+ ### By Property
185
+
186
+ ```python
187
+ graph = Graph.from_dict(
188
+ data,
189
+ node_color="group", # Color by field
190
+ node_size=lambda n: n["score"] * 10, # Size by function
191
+ edge_width="weight", # Width by field
192
+ )
193
+ ```
194
+
195
+ ### Layouts
196
+
197
+ ```python
198
+ Graph.from_dict(data, layout="force") # Default
199
+ Graph.from_dict(data, layout="hierarchical")
200
+ Graph.from_dict(data, layout="circular")
201
+ Graph.from_dict(data, layout="grid")
202
+ ```
203
+
204
+ ## Options
205
+
206
+ ```python
207
+ graph = Graph.from_dict(
208
+ data,
209
+ width=800,
210
+ height=600,
211
+ directed=True,
212
+ labels=True,
213
+ edge_labels=False,
214
+ physics=True,
215
+ zoom=(0.1, 4),
216
+ )
217
+ ```
218
+
219
+ ## Large Graphs
220
+
221
+ For 1000+ nodes:
222
+
223
+ ```python
224
+ graph = Graph.from_dict(
225
+ data,
226
+ virtualize=True,
227
+ cluster=True,
228
+ )
229
+ ```
230
+
231
+ ## Export
232
+
233
+ ```python
234
+ graph.to_png("graph.png")
235
+ graph.to_svg("graph.svg")
236
+ graph.to_json("graph.json")
237
+ ```
238
+
239
+ ## Environment Support
240
+
241
+ | Environment | Supported |
242
+ |-------------|-----------|
243
+ | Marimo | ✅ |
244
+ | JupyterLab | ✅ |
245
+ | Jupyter Notebook | ✅ |
246
+ | VS Code | ✅ |
247
+ | Google Colab | ✅ |
248
+ | Databricks | ✅ |
249
+
250
+ ## Related
251
+
252
+ - [anywidget](https://anywidget.dev/) — Custom Jupyter widgets made easy
253
+ - [Grafeo](https://github.com/GrafeoDB/grafeo) — Embeddable graph database
254
+ - [grafeo-wasm](https://github.com/GrafeoDB/grafeo-wasm) — Grafeo in the browser
255
+
256
+ ## License
257
+
258
+ Apache-2.0
@@ -0,0 +1,225 @@
1
+ # anywidget-graph
2
+
3
+ Interactive graph visualization for Python notebooks.
4
+
5
+ Works with Marimo, Jupyter, VS Code, Colab, anywhere [anywidget](https://anywidget.dev/) runs.
6
+
7
+ ## Features
8
+
9
+ - **Universal** — One widget, every notebook environment
10
+ - **Backend-agnostic** — Grafeo, Neo4j, NetworkX, pandas, or raw dicts
11
+ - **Interactive** — Pan, zoom, click, expand neighbors, select paths
12
+ - **Customizable** — Colors, sizes, shapes, layouts
13
+ - **Performant** — Virtualized rendering for large graphs
14
+ - **Exportable** — PNG, SVG, JSON
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ uv add anywidget-graph
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```python
25
+ from anywidget_graph import Graph
26
+
27
+ graph = Graph.from_dict({
28
+ "nodes": [
29
+ {"id": "alice", "label": "Alice", "group": "person"},
30
+ {"id": "bob", "label": "Bob", "group": "person"},
31
+ {"id": "paper", "label": "Graph Theory", "group": "document"},
32
+ ],
33
+ "edges": [
34
+ {"source": "alice", "target": "bob", "label": "knows"},
35
+ {"source": "alice", "target": "paper", "label": "authored"},
36
+ ]
37
+ })
38
+
39
+ graph
40
+ ```
41
+
42
+ ## Data Sources
43
+
44
+ ### Dictionary
45
+
46
+ ```python
47
+ from anywidget_graph import Graph
48
+
49
+ graph = Graph.from_dict({
50
+ "nodes": [{"id": "a"}, {"id": "b"}],
51
+ "edges": [{"source": "a", "target": "b"}]
52
+ })
53
+ ```
54
+
55
+ ### Grafeo
56
+
57
+ ```python
58
+ from grafeo import GrafeoDB
59
+ from anywidget_graph import Graph
60
+
61
+ db = GrafeoDB()
62
+ db.execute("INSERT (:Person {name: 'Alice'})-[:KNOWS]->(:Person {name: 'Bob'})")
63
+
64
+ result = db.execute("MATCH (a)-[r]->(b) RETURN a, r, b")
65
+ graph = Graph.from_grafeo(result)
66
+ ```
67
+
68
+ ### Neo4j
69
+
70
+ ```python
71
+ from neo4j import GraphDatabase
72
+ from anywidget_graph import Graph
73
+
74
+ driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))
75
+
76
+ with driver.session() as session:
77
+ result = session.run("MATCH (a)-[r]->(b) RETURN a, r, b LIMIT 100")
78
+ graph = Graph.from_neo4j(result)
79
+ ```
80
+
81
+ ### NetworkX
82
+
83
+ ```python
84
+ import networkx as nx
85
+ from anywidget_graph import Graph
86
+
87
+ G = nx.karate_club_graph()
88
+ graph = Graph.from_networkx(G)
89
+ ```
90
+
91
+ ### pandas
92
+
93
+ ```python
94
+ import pandas as pd
95
+ from anywidget_graph import Graph
96
+
97
+ edges = pd.DataFrame({
98
+ "source": ["alice", "alice", "bob"],
99
+ "target": ["bob", "carol", "carol"],
100
+ "weight": [1.0, 0.5, 0.8]
101
+ })
102
+
103
+ graph = Graph.from_pandas(edges)
104
+ ```
105
+
106
+ ## Interactivity
107
+
108
+ ### Events
109
+
110
+ ```python
111
+ graph = Graph.from_dict(data)
112
+
113
+ @graph.on_node_click
114
+ def handle_node(node_id, node_data):
115
+ print(f"Clicked: {node_id}")
116
+
117
+ @graph.on_edge_click
118
+ def handle_edge(edge_id, edge_data):
119
+ print(f"Edge: {edge_data['label']}")
120
+ ```
121
+
122
+ ### Selection
123
+
124
+ ```python
125
+ graph.selected_nodes # Get current selection
126
+ graph.select(["alice"]) # Select nodes
127
+ graph.clear_selection() # Clear
128
+ ```
129
+
130
+ ### Expansion
131
+
132
+ ```python
133
+ graph.expand("alice") # Show neighbors
134
+ graph.collapse("alice") # Hide neighbors
135
+ ```
136
+
137
+ ## Styling
138
+
139
+ ### By Group
140
+
141
+ ```python
142
+ graph = Graph.from_dict(
143
+ data,
144
+ node_styles={
145
+ "person": {"color": "#4CAF50", "size": 30},
146
+ "document": {"color": "#2196F3", "shape": "square"},
147
+ }
148
+ )
149
+ ```
150
+
151
+ ### By Property
152
+
153
+ ```python
154
+ graph = Graph.from_dict(
155
+ data,
156
+ node_color="group", # Color by field
157
+ node_size=lambda n: n["score"] * 10, # Size by function
158
+ edge_width="weight", # Width by field
159
+ )
160
+ ```
161
+
162
+ ### Layouts
163
+
164
+ ```python
165
+ Graph.from_dict(data, layout="force") # Default
166
+ Graph.from_dict(data, layout="hierarchical")
167
+ Graph.from_dict(data, layout="circular")
168
+ Graph.from_dict(data, layout="grid")
169
+ ```
170
+
171
+ ## Options
172
+
173
+ ```python
174
+ graph = Graph.from_dict(
175
+ data,
176
+ width=800,
177
+ height=600,
178
+ directed=True,
179
+ labels=True,
180
+ edge_labels=False,
181
+ physics=True,
182
+ zoom=(0.1, 4),
183
+ )
184
+ ```
185
+
186
+ ## Large Graphs
187
+
188
+ For 1000+ nodes:
189
+
190
+ ```python
191
+ graph = Graph.from_dict(
192
+ data,
193
+ virtualize=True,
194
+ cluster=True,
195
+ )
196
+ ```
197
+
198
+ ## Export
199
+
200
+ ```python
201
+ graph.to_png("graph.png")
202
+ graph.to_svg("graph.svg")
203
+ graph.to_json("graph.json")
204
+ ```
205
+
206
+ ## Environment Support
207
+
208
+ | Environment | Supported |
209
+ |-------------|-----------|
210
+ | Marimo | ✅ |
211
+ | JupyterLab | ✅ |
212
+ | Jupyter Notebook | ✅ |
213
+ | VS Code | ✅ |
214
+ | Google Colab | ✅ |
215
+ | Databricks | ✅ |
216
+
217
+ ## Related
218
+
219
+ - [anywidget](https://anywidget.dev/) — Custom Jupyter widgets made easy
220
+ - [Grafeo](https://github.com/GrafeoDB/grafeo) — Embeddable graph database
221
+ - [grafeo-wasm](https://github.com/GrafeoDB/grafeo-wasm) — Grafeo in the browser
222
+
223
+ ## License
224
+
225
+ Apache-2.0
@@ -0,0 +1,58 @@
1
+ """Marimo demo for anywidget-graph."""
2
+
3
+ import marimo
4
+
5
+ __generated_with = "0.19.5"
6
+ app = marimo.App(width="medium")
7
+
8
+
9
+ @app.cell
10
+ def _():
11
+ import marimo as mo
12
+
13
+ return (mo,)
14
+
15
+
16
+ @app.cell
17
+ def _():
18
+ from anywidget_graph import Graph
19
+
20
+ return (Graph,)
21
+
22
+
23
+ @app.cell
24
+ def _(Graph):
25
+ # Create a simple graph from a dictionary
26
+ graph = Graph.from_dict(
27
+ {
28
+ "nodes": [
29
+ {"id": "alice", "label": "Alice", "color": "#4CAF50"},
30
+ {"id": "bob", "label": "Bob", "color": "#2196F3"},
31
+ {"id": "carol", "label": "Carol", "color": "#FF9800"},
32
+ {"id": "paper", "label": "Graph Theory", "color": "#9C27B0"},
33
+ ],
34
+ "edges": [
35
+ {"source": "alice", "target": "bob", "label": "knows"},
36
+ {"source": "alice", "target": "carol", "label": "knows"},
37
+ {"source": "bob", "target": "carol", "label": "knows"},
38
+ {"source": "alice", "target": "paper", "label": "authored"},
39
+ {"source": "bob", "target": "paper", "label": "reviewed"},
40
+ ],
41
+ },
42
+ width=800,
43
+ height=500,
44
+ )
45
+
46
+ graph
47
+ return (graph,)
48
+
49
+
50
+ @app.cell
51
+ def _(graph, mo):
52
+ # Display selected node info
53
+ mo.md(f"**Selected node:** {graph.selected_node}")
54
+ return ()
55
+
56
+
57
+ if __name__ == "__main__":
58
+ app.run()
@@ -0,0 +1,87 @@
1
+ [project]
2
+ name = "anywidget-graph"
3
+ version = "0.1.0"
4
+ description = "Interactive graph visualization for Python notebooks using anywidget"
5
+ readme = "README.md"
6
+ license = { text = "Apache-2.0" }
7
+ requires-python = ">=3.12"
8
+ authors = [{ name = "S.T. Grond", email = "widget@grafeo.dev" }]
9
+ keywords = ["anywidget", "graph", "visualization", "jupyter", "marimo", "cypher", "neo4j"]
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Framework :: Jupyter",
13
+ "Intended Audience :: Developers",
14
+ "Intended Audience :: Science/Research",
15
+ "License :: OSI Approved :: Apache Software License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
20
+ "Topic :: Scientific/Engineering :: Visualization",
21
+ ]
22
+
23
+ dependencies = [
24
+ "anywidget>=0.9.21",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ dev = [
29
+ "prek>=0.3.1",
30
+ "pytest>=9.0.2",
31
+ "ruff>=0.14.14",
32
+ "ty>=0.0.14",
33
+ "marimo>=0.19.7",
34
+ ]
35
+ networkx = ["networkx>=3.0"]
36
+ pandas = ["pandas>=2.0"]
37
+
38
+ [project.urls]
39
+ Homepage = "https://grafeo.dev/"
40
+ Repository = "https://github.com/GrafeoDB/anywidget-graph"
41
+
42
+ [build-system]
43
+ requires = ["hatchling"]
44
+ build-backend = "hatchling.build"
45
+
46
+ [tool.hatch.build.targets.wheel]
47
+ packages = ["src/anywidget_graph"]
48
+
49
+ [tool.ruff]
50
+ line-length = 120
51
+ target-version = "py312"
52
+
53
+ [tool.ruff.lint]
54
+ select = [
55
+ "E",
56
+ "F",
57
+ "I",
58
+ "UP",
59
+ "FA",
60
+ "Q",
61
+ ]
62
+
63
+ [tool.ruff.lint.flake8-quotes]
64
+ inline-quotes = "double"
65
+ docstring-quotes = "double"
66
+
67
+ [tool.ty.environment]
68
+ python-version = "3.12"
69
+
70
+ [tool.pytest.ini_options]
71
+ testpaths = ["tests"]
72
+ pythonpath = ["src"]
73
+ addopts = "-v"
74
+
75
+ [tool.prek]
76
+ repos = [
77
+ { repo = "ruff", hooks = [
78
+ { id = "ruff-check", args = ["--fix", "--exit-non-zero-on-fix"] },
79
+ { id = "ruff-format" },
80
+ ]},
81
+ ]
82
+
83
+ [tool.uv]
84
+ dev-dependencies = [
85
+ "pytest>=9.0.2",
86
+ "ruff>=0.14.14",
87
+ ]
@@ -0,0 +1,6 @@
1
+ """Interactive graph visualization for Python notebooks."""
2
+
3
+ from anywidget_graph.widget import Graph
4
+
5
+ __all__ = ["Graph"]
6
+ __version__ = "0.1.0"
File without changes