runic-migrate 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,114 @@
1
+ __pycache__/
2
+ __pypackages__/
3
+ .cache
4
+ .coverage
5
+ .coverage.*
6
+ .dmypy.json
7
+ .DS_Store
8
+ .eggs/
9
+ .env
10
+ .env.backup
11
+ .env.docker
12
+ .hypothesis/
13
+ .idea/
14
+ .installed.cfg
15
+ .ipynb_checkpoints
16
+ .mypy_cache/
17
+ .nox/
18
+ .pdm.toml
19
+ .pybuilder/
20
+ .pyre/
21
+ .pytest_cache/
22
+ .Python
23
+ .python_packages
24
+ .pytype/
25
+ .ropeproject
26
+ .scrapy
27
+ .spyderproject
28
+ .spyproject
29
+ .states
30
+ .tox/
31
+ .venv
32
+ .venv.mac
33
+ .web
34
+ .webassets-cache
35
+ *.bak
36
+ *.cover
37
+ *.db
38
+ *.egg
39
+ *.egg-info/
40
+ *.kv-env.*
41
+ *.log
42
+ *.manifest
43
+ *.mo
44
+ *.pot
45
+ *.py,cover
46
+ *.py[cod]
47
+ *.sage.py
48
+ *.so
49
+ *.spec
50
+ *.terraform.lock.hcl
51
+ *.tfplan
52
+ *.tfstate
53
+ *.tfstate.*.backup
54
+ *.tfstate.backup
55
+ *.tfvars
56
+ **/.terraform/*
57
+ *$py.class
58
+ /site
59
+ /vectorstore/
60
+ aila-storage/
61
+ assets/external/
62
+ build/
63
+ celerybeat-schedule
64
+ celerybeat.pid
65
+ configuration/config.abaz009.yaml
66
+ configuration/config.bubb001.yaml
67
+ configuration/config.stie104.yaml
68
+ configuration/config.voro047.yaml
69
+ connector examples/sharepoint.json
70
+ cover/
71
+ coverage.xml
72
+ cython_debug/
73
+ db.sqlite3
74
+ db.sqlite3-journal
75
+ develop-eggs/
76
+ dist/
77
+ dmypy.json
78
+ docs/_build/
79
+ Documents/
80
+ downloads/
81
+ eggs/
82
+ env.bak/
83
+ env/
84
+ ENV/
85
+ htmlcov/
86
+ instance/
87
+ ipython_config.py
88
+ knowledge/migrate.py
89
+ lib/
90
+ lib64/
91
+ local_settings.py
92
+ local.settings.json
93
+ MANIFEST
94
+ nosetests.xml
95
+ out
96
+ parts/
97
+ pip-delete-this-directory.txt
98
+ pip-log.txt
99
+ Pipfile
100
+ profile_default/
101
+ sdist/
102
+ share/python-wheels/
103
+ sketchpad/
104
+ sketchpad/
105
+ stores/
106
+ target/
107
+ tests/mcp_test.py
108
+ tmp.txt
109
+ uploaded_files/
110
+ uploads/
111
+ var/
112
+ venv.bak/
113
+ venv/
114
+ wheels/
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2025 Jens Rehpöhler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: runic-migrate
3
+ Version: 0.1.0
4
+ Summary: Graph schema migrations for FalkorDB.
5
+ Project-URL: Homepage, https://github.com/jenreh/python-kit
6
+ Project-URL: Repository, https://github.com/jenreh/python-kit
7
+ Author: Jens Rehpöhler
8
+ License: MIT
9
+ License-File: LICENSE.md
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Topic :: Home Automation
15
+ Requires-Python: >=3.14
16
+ Requires-Dist: aiosqlite==0.22.1
17
+ Requires-Dist: falkordb>=1.6.1
18
+ Requires-Dist: falkordblite>=0.10.0
19
+ Requires-Dist: fastmcp==3.3.1
20
+ Requires-Dist: mako>=1.3.12
21
+ Requires-Dist: pydantic-settings==2.14.1
22
+ Requires-Dist: pydantic==2.13.4
23
+ Requires-Dist: rich==15.0.0
24
+ Requires-Dist: typer[all]==0.26.3
25
+ Description-Content-Type: text/markdown
26
+
27
+ <div align="center">
28
+ <img src="docs/source/_static/runic.svg" width="240" alt="Runic logo">
29
+
30
+ # Runic
31
+
32
+ **Graph schema migrations for FalkorDB.**
33
+
34
+ ![Version](https://img.shields.io/badge/version-0.1.0-blue)
35
+ [![pypi](https://img.shields.io/pypi/v/runic.svg)](https://pypi.python.org/pypi/runic)
36
+ [![Python](https://img.shields.io/badge/python-3.14%2B-orange)](https://www.python.org)
37
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE.md)
38
+
39
+ [Features](#features) • [Installation](#installation) • [A Simple Example](#a-simple-example) • [Documentation](#documentation)
40
+
41
+ </div>
42
+
43
+ ---
44
+
45
+ **Runic** is a lightweight, Alembic-style migration framework built specifically for [FalkorDB](https://falkordb.com/).
46
+ It brings robust revision tracking, linear graph migrations, and a powerful CLI to graph database environments, managing schema versioning through Cypher scripts and native FalkorDB syntax.
47
+
48
+ ## Features
49
+
50
+ - **Alembic-Style Workflow** — Familiar CLI verbs like `init`, `revision`, `upgrade`, `downgrade`, and `current`.
51
+ - **Graph-Native** — Treats your database as a graph. Stores migration states intelligently inside dedicated nodes (e.g., `:_FalkorMigrateVersion`).
52
+ - **Idempotent Cypher** — Encourages explicit, heavily-guarded migration steps, supporting robust backward capability even without transactional DDLs.
53
+ - **Offline & Dry Run** — Review generated Cypher scripts thoroughly before executing them in production.
54
+ - **Rollback Snapshots** — Advanced capabilities utilizing `GRAPH.COPY` for high-risk, non-reversible data migrations.
55
+
56
+ ## Installation
57
+
58
+ Install via `pip` or `uv`:
59
+
60
+ ```bash
61
+ uv pip install runic
62
+ ```
63
+
64
+ Or add it to an existing project:
65
+
66
+ ```bash
67
+ uv add runic
68
+ ```
69
+
70
+ > [!NOTE]
71
+ > Runic requires Python 3.14+ and is optimized for the latest FalkorDB clients.
72
+
73
+ ## A Simple Example
74
+
75
+ Initialize your project and generate a new revision:
76
+
77
+ ```bash
78
+ # Set up a new runic environment
79
+ runic init
80
+
81
+ # Create your first revision script
82
+ runic revision -m "create user index"
83
+ ```
84
+
85
+ This generates a revision file in `runic/versions`. Open it and define your upgrades and downgrades:
86
+
87
+ ```python
88
+ """create user index
89
+
90
+ Revision ID: 1975ea83b712
91
+ Revises: None
92
+ Create Date: 2026-05-30 14:00:00.000000
93
+ """
94
+
95
+ from runic import op
96
+
97
+ # revision identifiers, used by Runic.
98
+ revision = "1975ea83b712"
99
+ down_revision = None
100
+
101
+
102
+ def upgrade() -> None:
103
+ # Use Cypher to create an index
104
+ op.run_cypher("CREATE INDEX ON :User(email)")
105
+
106
+
107
+ def downgrade() -> None:
108
+ # Safely revert the operation
109
+ op.run_cypher("DROP INDEX ON :User(email)")
110
+ ```
111
+
112
+ Then apply your changes:
113
+
114
+ ```bash
115
+ runic upgrade head
116
+ ```
117
+
118
+ ## Documentation
119
+
120
+ For a full conceptual overview, advanced CLI usage, and deep dives into branching or multi-head resolution, visit the complete [Runic Documentation](https://jenreh.github.io/runic/).
@@ -0,0 +1,94 @@
1
+ <div align="center">
2
+ <img src="docs/source/_static/runic.svg" width="240" alt="Runic logo">
3
+
4
+ # Runic
5
+
6
+ **Graph schema migrations for FalkorDB.**
7
+
8
+ ![Version](https://img.shields.io/badge/version-0.1.0-blue)
9
+ [![pypi](https://img.shields.io/pypi/v/runic.svg)](https://pypi.python.org/pypi/runic)
10
+ [![Python](https://img.shields.io/badge/python-3.14%2B-orange)](https://www.python.org)
11
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE.md)
12
+
13
+ [Features](#features) • [Installation](#installation) • [A Simple Example](#a-simple-example) • [Documentation](#documentation)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ **Runic** is a lightweight, Alembic-style migration framework built specifically for [FalkorDB](https://falkordb.com/).
20
+ It brings robust revision tracking, linear graph migrations, and a powerful CLI to graph database environments, managing schema versioning through Cypher scripts and native FalkorDB syntax.
21
+
22
+ ## Features
23
+
24
+ - **Alembic-Style Workflow** — Familiar CLI verbs like `init`, `revision`, `upgrade`, `downgrade`, and `current`.
25
+ - **Graph-Native** — Treats your database as a graph. Stores migration states intelligently inside dedicated nodes (e.g., `:_FalkorMigrateVersion`).
26
+ - **Idempotent Cypher** — Encourages explicit, heavily-guarded migration steps, supporting robust backward capability even without transactional DDLs.
27
+ - **Offline & Dry Run** — Review generated Cypher scripts thoroughly before executing them in production.
28
+ - **Rollback Snapshots** — Advanced capabilities utilizing `GRAPH.COPY` for high-risk, non-reversible data migrations.
29
+
30
+ ## Installation
31
+
32
+ Install via `pip` or `uv`:
33
+
34
+ ```bash
35
+ uv pip install runic
36
+ ```
37
+
38
+ Or add it to an existing project:
39
+
40
+ ```bash
41
+ uv add runic
42
+ ```
43
+
44
+ > [!NOTE]
45
+ > Runic requires Python 3.14+ and is optimized for the latest FalkorDB clients.
46
+
47
+ ## A Simple Example
48
+
49
+ Initialize your project and generate a new revision:
50
+
51
+ ```bash
52
+ # Set up a new runic environment
53
+ runic init
54
+
55
+ # Create your first revision script
56
+ runic revision -m "create user index"
57
+ ```
58
+
59
+ This generates a revision file in `runic/versions`. Open it and define your upgrades and downgrades:
60
+
61
+ ```python
62
+ """create user index
63
+
64
+ Revision ID: 1975ea83b712
65
+ Revises: None
66
+ Create Date: 2026-05-30 14:00:00.000000
67
+ """
68
+
69
+ from runic import op
70
+
71
+ # revision identifiers, used by Runic.
72
+ revision = "1975ea83b712"
73
+ down_revision = None
74
+
75
+
76
+ def upgrade() -> None:
77
+ # Use Cypher to create an index
78
+ op.run_cypher("CREATE INDEX ON :User(email)")
79
+
80
+
81
+ def downgrade() -> None:
82
+ # Safely revert the operation
83
+ op.run_cypher("DROP INDEX ON :User(email)")
84
+ ```
85
+
86
+ Then apply your changes:
87
+
88
+ ```bash
89
+ runic upgrade head
90
+ ```
91
+
92
+ ## Documentation
93
+
94
+ For a full conceptual overview, advanced CLI usage, and deep dives into branching or multi-head resolution, visit the complete [Runic Documentation](https://jenreh.github.io/runic/).
@@ -0,0 +1,256 @@
1
+ [project]
2
+ name = "runic-migrate"
3
+ version = "0.1.0"
4
+ description = "Graph schema migrations for FalkorDB."
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "Jens Rehpöhler" }]
9
+ keywords = []
10
+ classifiers = [
11
+ "Intended Audience :: Developers",
12
+ "License :: OSI Approved :: MIT License",
13
+ "Programming Language :: Python :: 3",
14
+ "Programming Language :: Python :: 3 :: Only",
15
+ "Topic :: Home Automation",
16
+ ]
17
+
18
+ dependencies = [
19
+ "fastmcp==3.3.1",
20
+ "typer[all]==0.26.3",
21
+ "rich==15.0.0",
22
+ "pydantic==2.13.4",
23
+ "pydantic-settings==2.14.1",
24
+ "aiosqlite==0.22.1",
25
+ "falkordb>=1.6.1",
26
+ "mako>=1.3.12",
27
+ "falkordblite>=0.10.0",
28
+ ]
29
+
30
+ [dependency-groups]
31
+ dev = [
32
+ "bump-my-version==1.3.0",
33
+ "detect-secrets==1.5.0",
34
+ "pre-commit==4.6.0",
35
+ "pytest==9.0.3",
36
+ "pytest-asyncio==1.4.0",
37
+ "pytest-cov==7.1.0",
38
+ "pytest-httpserver==1.1.5",
39
+ "ruff==0.15.15",
40
+ "mypy==2.1.0",
41
+ "types-PyYAML==6.0.12.20260510",
42
+ "falkordblite>=0.10.0",
43
+ "sphinx>=9.1.0",
44
+ "furo>=2025.12.19",
45
+ "sphinx-copybutton>=0.5.2",
46
+ "sphinx-design>=0.7.0",
47
+ ]
48
+
49
+ [project.scripts]
50
+ runic = "runic.cli:app"
51
+
52
+ [project.urls]
53
+ Homepage = "https://github.com/jenreh/python-kit"
54
+ Repository = "https://github.com/jenreh/python-kit"
55
+
56
+ [tool.hatch.build.targets.wheel]
57
+ packages = ["runic"]
58
+
59
+ [tool.hatch.build.targets.sdist]
60
+ include = [
61
+ "/runic",
62
+ "README.md",
63
+ "pyproject.toml",
64
+ ]
65
+ exclude = [
66
+ ".agents/",
67
+ ".cache/",
68
+ ".claude/",
69
+ ".devcontainer/",
70
+ ".github/",
71
+ ".vscode/",
72
+ "skill/",
73
+ "spec/",
74
+ "tests/",
75
+ ".env",
76
+ ".gitignore",
77
+ ".pre-commit-config.yaml",
78
+ "AGENTS.md",
79
+ "coverage.xml",
80
+ "Taskfile.yaml",
81
+ ]
82
+
83
+ [tool.bumpversion]
84
+ commit = false
85
+ current_version = "0.1.0"
86
+ tag = false
87
+ allow-dirty = false
88
+
89
+ [[tool.bumpversion.files]]
90
+ filename = "pyproject.toml"
91
+ search = 'version = "{current_version}"'
92
+ replace = 'version = "{new_version}"'
93
+
94
+ [[tool.bumpversion.files]]
95
+ filename = "README.md"
96
+ search = '![Version](https://img.shields.io/badge/version-{current_version}-blue)'
97
+ replace = '![Version](https://img.shields.io/badge/version-{new_version}-blue)'
98
+
99
+ [tool.ruff]
100
+ cache-dir = ".cache/ruff"
101
+ indent-width = 4
102
+ line-length = 88
103
+ target-version = "py314"
104
+ extend-exclude = [
105
+ ".agents",
106
+ ".cache",
107
+ ".claude",
108
+ ".devcontainer",
109
+ ".github",
110
+ ".vscode",
111
+ "build",
112
+ "dist",
113
+ "docs",
114
+ ]
115
+
116
+ [tool.ruff.lint]
117
+ extend-select = ["I"]
118
+ ignore = [
119
+ "EM101",
120
+ "EM102",
121
+ "ISC001",
122
+ "N811",
123
+ "N818",
124
+ "PLR0913",
125
+ "PT011",
126
+ "PT012",
127
+ "RUF012",
128
+ "RUF100",
129
+ "SIM300",
130
+ "E501",
131
+ "B008",
132
+ "ASYNC240",
133
+ "PYI034",
134
+ "S101",
135
+ "PLR0912",
136
+ "PLR0915",
137
+ "ASYNC109",
138
+ "ARG001",
139
+ "PLR2004",
140
+ "PLW0603",
141
+ "PLC0415",
142
+ "SIM105",
143
+ "PLW2901",
144
+ "PLR0911",
145
+ "B017",
146
+ "RUF001",
147
+ "RUF002",
148
+ "RUF003",
149
+ ]
150
+ select = [
151
+ "A",
152
+ "ANN001",
153
+ "ANN201",
154
+ "ARG",
155
+ "ASYNC",
156
+ "B",
157
+ "C4",
158
+ "DTZ",
159
+ "E",
160
+ "EM",
161
+ "EXE",
162
+ "F",
163
+ "FIX",
164
+ "G",
165
+ "ICN",
166
+ "LOG",
167
+ "N",
168
+ "PERF",
169
+ "PIE",
170
+ "PL",
171
+ "PT",
172
+ "PTH",
173
+ "PYI",
174
+ "Q",
175
+ "RET",
176
+ "RUF",
177
+ "S",
178
+ "SIM",
179
+ "SLF",
180
+ "SLF001",
181
+ "T20",
182
+ "TID",
183
+ "UP",
184
+ "W",
185
+ ]
186
+
187
+ [tool.ruff.lint.per-file-ignores]
188
+ "__init__.py" = ["F401", "I001"]
189
+ "test_**.py" = [
190
+ "S101",
191
+ "PLR2004",
192
+ "ANN001",
193
+ "S106",
194
+ "SLF001",
195
+ "RET504",
196
+ "PERF401",
197
+ "E402",
198
+ "S501",
199
+ "S110",
200
+ "ARG002",
201
+ "ARG005",
202
+ "E741",
203
+ "RUF059",
204
+ ]
205
+
206
+ [tool.ruff.format]
207
+ quote-style = "double"
208
+ indent-style = "space"
209
+ skip-magic-trailing-comma = false
210
+ line-ending = "auto"
211
+ docstring-code-format = true
212
+ docstring-code-line-length = "dynamic"
213
+ exclude = ["*.pyi"]
214
+
215
+ [tool.ruff.lint.isort]
216
+ known-local-folder = ["sonos"]
217
+
218
+ [tool.pytest.ini_options]
219
+ cache_dir = ".cache/pytest"
220
+ testpaths = ["tests"]
221
+ asyncio_mode = "auto"
222
+ markers = [
223
+ "sonos_live: requires real Sonos hardware on the network",
224
+ "integration: requires falkordblite (embedded FalkorDB, no external server)",
225
+ ]
226
+
227
+ [tool.mypy]
228
+ cache_dir = ".cache/mypy"
229
+ disallow_untyped_defs = true
230
+ incremental = true
231
+ python_version = "3.14"
232
+ warn_return_any = true
233
+ warn_unused_configs = true
234
+ ignore_missing_imports = true
235
+ exclude = [
236
+ ".cache",
237
+ ".claude",
238
+ ".venv",
239
+ "build",
240
+ "dist",
241
+ ]
242
+
243
+ [tool.coverage.run]
244
+ branch = true
245
+ source = ["app", "runic"]
246
+ omit = []
247
+
248
+ [tool.coverage.report]
249
+ exclude_lines = ["if TYPE_CHECKING:", "if __name__ == .__main__.:", "pragma: no cover"]
250
+ fail_under = 80
251
+ show_missing = true
252
+ skip_covered = true
253
+
254
+ [build-system]
255
+ requires = ["hatchling"]
256
+ build-backend = "hatchling.build"
@@ -0,0 +1,33 @@
1
+ from runic import context
2
+ from runic.context import IrreversibleMigrationError
3
+ from runic.exceptions import MultipleBasesError, MultipleHeadsError
4
+ from runic.manifest import (
5
+ FulltextIndex,
6
+ MandatoryConstraint,
7
+ RangeIndex,
8
+ SchemaManifest,
9
+ UniqueConstraint,
10
+ VectorIndex,
11
+ )
12
+ from runic.operations import ConstraintFailedError, ConstraintTimeoutError, op
13
+ from runic.script import AmbiguousRevision, RevisionNotFound
14
+ from runic.service import RunicService
15
+
16
+ __all__ = [
17
+ "AmbiguousRevision",
18
+ "ConstraintFailedError",
19
+ "ConstraintTimeoutError",
20
+ "FulltextIndex",
21
+ "IrreversibleMigrationError",
22
+ "MandatoryConstraint",
23
+ "MultipleBasesError",
24
+ "MultipleHeadsError",
25
+ "RangeIndex",
26
+ "RevisionNotFound",
27
+ "RunicService",
28
+ "SchemaManifest",
29
+ "UniqueConstraint",
30
+ "VectorIndex",
31
+ "context",
32
+ "op",
33
+ ]
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Protocol, runtime_checkable
4
+
5
+ from runic.introspect import LiveSchema
6
+
7
+
8
+ @runtime_checkable
9
+ class GraphAdapter(Protocol):
10
+ """Protocol all graph-database adapters must satisfy.
11
+
12
+ The runic core depends only on this interface — no FalkorDB or any other
13
+ concrete database client leaks into shared code.
14
+ """
15
+
16
+ @property
17
+ def name(self) -> str: ...
18
+
19
+ # Low-level query execution
20
+ def run_query(self, query: str, params: dict | None = None) -> Any: ...
21
+ def run_ro_query(self, query: str) -> Any: ...
22
+
23
+ # Version tracking
24
+ def get_version(self) -> list[str]: ...
25
+ def set_version(self, revisions: list[str]) -> None: ...
26
+
27
+ # Schema introspection
28
+ def read_live_schema(self) -> LiveSchema: ...
29
+
30
+ # Schema DDL
31
+ def create_range_index(
32
+ self, label: str, prop: str, *, rel: bool = False
33
+ ) -> None: ...
34
+ def drop_range_index(self, label: str, prop: str, *, rel: bool = False) -> None: ...
35
+ def create_fulltext_index(
36
+ self,
37
+ label: str,
38
+ *props: str,
39
+ language: str | None = None,
40
+ stopwords: list[str] | None = None,
41
+ ) -> None: ...
42
+ def drop_fulltext_index(self, label: str, *props: str) -> None: ...
43
+ def create_vector_index(
44
+ self,
45
+ label: str,
46
+ prop: str,
47
+ dimension: int,
48
+ similarity: str,
49
+ *,
50
+ m: int = 16,
51
+ ef_construction: int = 200,
52
+ ef_runtime: int = 10,
53
+ ) -> None: ...
54
+ def drop_vector_index(self, label: str, prop: str) -> None: ...
55
+ def create_constraint(
56
+ self, kind: str, entity: str, label: str, props: list[str]
57
+ ) -> None: ...
58
+ def drop_constraint(
59
+ self, kind: str, entity: str, label: str, props: list[str]
60
+ ) -> None: ...
61
+
62
+ # Snapshots
63
+ def snapshot(self, snap_name: str) -> None: ...
64
+ def restore_snapshot(self, snap_name: str) -> None: ...
65
+ def snapshot_exists(self, snap_name: str) -> bool: ...
66
+
67
+
68
+ __all__ = ["GraphAdapter"]