cinchdb 0.1.14__py3-none-any.whl → 0.1.17__py3-none-any.whl

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,80 @@
1
+ """
2
+ Simple base class for CinchDB plugins.
3
+ """
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+
8
+ class Plugin:
9
+ """Simple base class for CinchDB plugins."""
10
+
11
+ # Plugin metadata - override in subclass
12
+ name: str = "unnamed_plugin"
13
+ version: str = "1.0.0"
14
+ description: str = ""
15
+
16
+ def extend_database(self, db) -> None:
17
+ """Add methods or modify the database instance.
18
+
19
+ Override this method to add custom methods to database instances:
20
+
21
+ Example:
22
+ def extend_database(self, db):
23
+ db.my_custom_method = self.my_custom_method
24
+ """
25
+ pass
26
+
27
+ def before_query(self, sql: str, params: Optional[tuple] = None) -> tuple:
28
+ """Called before executing any SQL query.
29
+
30
+ Args:
31
+ sql: The SQL statement to be executed
32
+ params: Parameters for the SQL statement
33
+
34
+ Returns:
35
+ Tuple of (modified_sql, modified_params)
36
+ """
37
+ return sql, params
38
+
39
+ def after_query(self, sql: str, params: Optional[tuple], result: Any) -> Any:
40
+ """Called after executing any SQL query.
41
+
42
+ Args:
43
+ sql: The SQL statement that was executed
44
+ params: Parameters that were used
45
+ result: The query result
46
+
47
+ Returns:
48
+ Modified result (or original result)
49
+ """
50
+ return result
51
+
52
+ def on_connect(self, db_path: str, connection) -> None:
53
+ """Called when a database connection is established.
54
+
55
+ Args:
56
+ db_path: Path to the database file
57
+ connection: SQLite connection object
58
+ """
59
+ pass
60
+
61
+ def on_disconnect(self, db_path: str) -> None:
62
+ """Called when a database connection is closed.
63
+
64
+ Args:
65
+ db_path: Path to the database file
66
+ """
67
+ pass
68
+
69
+ def cleanup(self) -> None:
70
+ """Called when plugin is being unloaded."""
71
+ pass
72
+
73
+ @property
74
+ def metadata(self) -> Dict[str, Any]:
75
+ """Get plugin metadata."""
76
+ return {
77
+ "name": self.name,
78
+ "version": self.version,
79
+ "description": self.description,
80
+ }
@@ -0,0 +1,49 @@
1
+ """
2
+ Simple decorators for plugin development.
3
+ """
4
+
5
+ from typing import Callable
6
+
7
+
8
+ def database_method(method_name: str):
9
+ """Decorator to mark a method for database extension.
10
+
11
+ Usage:
12
+ class Plugin:
13
+ @database_method("my_method")
14
+ def my_custom_method(self, db):
15
+ return "Hello from plugin!"
16
+ """
17
+ def decorator(func: Callable) -> Callable:
18
+ func._database_method_name = method_name
19
+ return func
20
+ return decorator
21
+
22
+
23
+ def auto_extend(plugin_class):
24
+ """Class decorator to automatically extend databases with decorated methods.
25
+
26
+ Usage:
27
+ @auto_extend
28
+ class Plugin:
29
+ @database_method("custom_query")
30
+ def custom_query_method(self, db, query):
31
+ # Method will be added to db instances as db.custom_query()
32
+ return "Custom result"
33
+ """
34
+ original_extend = getattr(plugin_class, 'extend_database', None)
35
+
36
+ def new_extend_database(self, db):
37
+ # Call original extend_database if it exists
38
+ if original_extend:
39
+ original_extend(self, db)
40
+
41
+ # Auto-add decorated methods
42
+ for attr_name in dir(self):
43
+ attr = getattr(self, attr_name)
44
+ if callable(attr) and hasattr(attr, '_database_method_name'):
45
+ method_name = attr._database_method_name
46
+ setattr(db, method_name, lambda *args, **kwargs: attr(db, *args, **kwargs))
47
+
48
+ plugin_class.extend_database = new_extend_database
49
+ return plugin_class
@@ -0,0 +1,210 @@
1
+ """
2
+ Simple plugin manager for CinchDB.
3
+ """
4
+
5
+ import importlib
6
+ import importlib.util
7
+ import logging
8
+ from pathlib import Path
9
+ from typing import Dict, List, Optional, Any, Union
10
+
11
+ from .base import Plugin
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class PluginManager:
17
+ """Simple plugin manager for CinchDB."""
18
+
19
+ def __init__(self):
20
+ self.plugins: Dict[str, Plugin] = {}
21
+ self._database_instances: List[Any] = []
22
+
23
+ def register_plugin(self, plugin: Union[Plugin, type]) -> None:
24
+ """Register a plugin instance or class."""
25
+ # If it's a class, instantiate it
26
+ if isinstance(plugin, type):
27
+ plugin = plugin()
28
+
29
+ plugin_name = plugin.name
30
+
31
+ if plugin_name in self.plugins:
32
+ logger.warning(f"Plugin {plugin_name} already registered, replacing")
33
+
34
+ self.plugins[plugin_name] = plugin
35
+
36
+ # Apply to existing database instances
37
+ for db_instance in self._database_instances:
38
+ try:
39
+ plugin.extend_database(db_instance)
40
+ except Exception as e:
41
+ logger.error(f"Failed to extend database with plugin {plugin_name}: {e}")
42
+
43
+ logger.info(f"Plugin {plugin_name} registered successfully")
44
+
45
+ def unregister_plugin(self, plugin_name: str) -> None:
46
+ """Unregister a plugin."""
47
+ if plugin_name in self.plugins:
48
+ plugin = self.plugins[plugin_name]
49
+ try:
50
+ plugin.cleanup()
51
+ except Exception as e:
52
+ logger.error(f"Error cleaning up plugin {plugin_name}: {e}")
53
+
54
+ del self.plugins[plugin_name]
55
+ logger.info(f"Plugin {plugin_name} unregistered")
56
+
57
+ def load_plugin_from_module(self, module_name: str) -> None:
58
+ """Load a plugin from a module name."""
59
+ try:
60
+ module = importlib.import_module(module_name)
61
+
62
+ # Look for Plugin class first
63
+ if hasattr(module, 'Plugin'):
64
+ plugin_instance = module.Plugin()
65
+ self.register_plugin(plugin_instance)
66
+ return
67
+
68
+ # Fallback: look for any Plugin subclass
69
+ for attr_name in dir(module):
70
+ attr = getattr(module, attr_name)
71
+ if (isinstance(attr, type) and
72
+ issubclass(attr, Plugin) and
73
+ attr != Plugin):
74
+
75
+ plugin_instance = attr()
76
+ self.register_plugin(plugin_instance)
77
+ return
78
+
79
+ logger.warning(f"No Plugin class found in module {module_name}")
80
+
81
+ except ImportError as e:
82
+ logger.error(f"Failed to import plugin module {module_name}: {e}")
83
+ except Exception as e:
84
+ logger.error(f"Failed to load plugin from {module_name}: {e}")
85
+
86
+ def load_plugin_from_file(self, file_path: Path) -> None:
87
+ """Load a plugin from a Python file."""
88
+ try:
89
+ spec = importlib.util.spec_from_file_location("plugin_module", file_path)
90
+ if spec and spec.loader:
91
+ module = importlib.util.module_from_spec(spec)
92
+ spec.loader.exec_module(module)
93
+
94
+ # Look for Plugin class
95
+ if hasattr(module, 'Plugin'):
96
+ plugin_instance = module.Plugin()
97
+ self.register_plugin(plugin_instance)
98
+ return
99
+
100
+ logger.warning(f"No Plugin class found in file {file_path}")
101
+
102
+ except Exception as e:
103
+ logger.error(f"Failed to load plugin from file {file_path}: {e}")
104
+
105
+ def load_plugins_from_directory(self, plugins_dir: Path) -> None:
106
+ """Load all plugins from a directory."""
107
+ if not plugins_dir.exists():
108
+ logger.info(f"Plugins directory {plugins_dir} does not exist")
109
+ return
110
+
111
+ for plugin_file in plugins_dir.glob("*.py"):
112
+ if plugin_file.name == "__init__.py":
113
+ continue
114
+ self.load_plugin_from_file(plugin_file)
115
+
116
+ def discover_plugins(self) -> None:
117
+ """Discover plugins using entry points and plugins directory."""
118
+ # Try entry points for installed plugins
119
+ try:
120
+ try:
121
+ from importlib.metadata import entry_points
122
+ except ImportError:
123
+ from importlib_metadata import entry_points
124
+
125
+ eps = entry_points()
126
+ if hasattr(eps, 'select'):
127
+ plugin_eps = eps.select(group='cinchdb.plugins')
128
+ else:
129
+ plugin_eps = eps.get('cinchdb.plugins', [])
130
+
131
+ for entry_point in plugin_eps:
132
+ try:
133
+ plugin_class = entry_point.load()
134
+ self.register_plugin(plugin_class)
135
+ except Exception as e:
136
+ logger.error(f"Failed to load plugin {entry_point.name}: {e}")
137
+ except Exception as e:
138
+ logger.debug(f"Entry points not available: {e}")
139
+
140
+ # Also check for local plugins directory
141
+ plugins_dir = Path("plugins")
142
+ if plugins_dir.exists():
143
+ self.load_plugins_from_directory(plugins_dir)
144
+
145
+ def register_database(self, db_instance) -> None:
146
+ """Register a database instance with all plugins."""
147
+ self._database_instances.append(db_instance)
148
+
149
+ # Apply all plugins to this database instance
150
+ for plugin in self.plugins.values():
151
+ try:
152
+ plugin.extend_database(db_instance)
153
+ except Exception as e:
154
+ logger.error(f"Failed to extend database with plugin {plugin.name}: {e}")
155
+
156
+ def unregister_database(self, db_instance) -> None:
157
+ """Unregister a database instance."""
158
+ if db_instance in self._database_instances:
159
+ self._database_instances.remove(db_instance)
160
+
161
+ def before_query(self, sql: str, params: Optional[tuple] = None) -> tuple:
162
+ """Call before_query on all plugins."""
163
+ for plugin in self.plugins.values():
164
+ try:
165
+ sql, params = plugin.before_query(sql, params)
166
+ except Exception as e:
167
+ logger.error(f"Plugin {plugin.name} before_query failed: {e}")
168
+ return sql, params
169
+
170
+ def after_query(self, sql: str, params: Optional[tuple], result: Any) -> Any:
171
+ """Call after_query on all plugins."""
172
+ for plugin in self.plugins.values():
173
+ try:
174
+ result = plugin.after_query(sql, params, result)
175
+ except Exception as e:
176
+ logger.error(f"Plugin {plugin.name} after_query failed: {e}")
177
+ return result
178
+
179
+ def on_connect(self, db_path: str, connection) -> None:
180
+ """Call on_connect on all plugins."""
181
+ for plugin in self.plugins.values():
182
+ try:
183
+ plugin.on_connect(db_path, connection)
184
+ except Exception as e:
185
+ logger.error(f"Plugin {plugin.name} on_connect failed: {e}")
186
+
187
+ def on_disconnect(self, db_path: str) -> None:
188
+ """Call on_disconnect on all plugins."""
189
+ for plugin in self.plugins.values():
190
+ try:
191
+ plugin.on_disconnect(db_path)
192
+ except Exception as e:
193
+ logger.error(f"Plugin {plugin.name} on_disconnect failed: {e}")
194
+
195
+ def get_plugin(self, name: str) -> Optional[Plugin]:
196
+ """Get a plugin by name."""
197
+ return self.plugins.get(name)
198
+
199
+ def list_plugins(self) -> List[Dict[str, Any]]:
200
+ """List all registered plugins with their metadata."""
201
+ return [plugin.metadata for plugin in self.plugins.values()]
202
+
203
+ def plugin_exists(self, name: str) -> bool:
204
+ """Check if a plugin is registered."""
205
+ return name in self.plugins
206
+
207
+ def cleanup_all(self) -> None:
208
+ """Cleanup all plugins."""
209
+ for plugin_name in list(self.plugins.keys()):
210
+ self.unregister_plugin(plugin_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cinchdb
3
- Version: 0.1.14
3
+ Version: 0.1.17
4
4
  Summary: A Git-like SQLite database management system with branching and multi-tenancy
5
5
  Project-URL: Homepage, https://github.com/russellromney/cinchdb
6
6
  Project-URL: Documentation, https://russellromney.github.io/cinchdb
@@ -31,6 +31,10 @@ Description-Content-Type: text/markdown
31
31
 
32
32
  **Git-like SQLite database management with branching and multi-tenancy**
33
33
 
34
+ [![PyPI version](https://badge.fury.io/py/cinchdb.svg)](https://badge.fury.io/py/cinchdb)
35
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
36
+
37
+
34
38
  NOTE: CinchDB is in early alpha. This is project to test out an idea. Do not use this in production.
35
39
 
36
40
  CinchDB is for projects that need fast queries, isolated data per-tenant [or even per-user](https://turso.tech/blog/give-each-of-your-users-their-own-sqlite-database-b74445f4), and a branchable database that makes it easy to merge changes between branches.
@@ -156,24 +160,16 @@ db.update("posts", post_id, {"content": "Updated content"})
156
160
  - **Python SDK**: Core functionality for local development
157
161
  - **CLI**: Full-featured command-line interface
158
162
 
159
- ## Security & Encryption
163
+ ## Security
160
164
 
161
- Optional transparent encryption for tenant databases:
165
+ CinchDB uses standard SQLite security features:
162
166
 
163
- ```bash
164
- # Enable encryption
165
- export CINCH_ENCRYPT_DATA=true
166
- export CINCH_ENCRYPTION_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')"
167
+ - **WAL mode**: Better concurrency and crash recovery
168
+ - **Foreign key constraints**: Enforced data integrity
169
+ - **File permissions**: Standard OS-level access control
170
+ - **Multi-tenant isolation**: Separate database files per tenant
167
171
 
168
- # Install encryption library
169
- pip install pysqlcipher3
170
- ```
171
-
172
- - **Tenant databases**: Encrypted with ChaCha20-Poly1305 (~2-5% overhead)
173
- - **Metadata**: Unencrypted for operational simplicity
174
- - **Integration**: Transparent - no code changes needed
175
-
176
- Works without encryption libraries - gracefully falls back to standard SQLite.
172
+ For production deployments, consider additional security measures at the infrastructure level.
177
173
 
178
174
  ## Development
179
175
 
@@ -186,14 +182,13 @@ make test
186
182
 
187
183
  ## Future
188
184
 
189
- Though probably not, perhaps I'll evolve it into something bigger and more full-featured, with things like
190
- - data backups
191
- - replication to S3
192
- - audit access
193
- - SaaS-like dynamics
194
- - multi-project hosting
195
- - auth proxying
196
- - leader-follower abilities for edge deployment
185
+ CinchDB focuses on being a simple, reliable SQLite management layer. Future development will prioritize:
186
+
187
+ - Remote API server improvements
188
+ - Better CLI user experience
189
+ - Performance optimizations
190
+ - Additional language SDKs (TypeScript, Go, etc.)
191
+ - Enhanced codegen features
197
192
 
198
193
 
199
194
  ## License
@@ -1,44 +1,46 @@
1
- cinchdb/__init__.py,sha256=m-yJkJh1kFCBWHjgEi9Aqz7LrrxlhRWc13Daa8PJv74,477
1
+ cinchdb/__init__.py,sha256=qnBv5kpL4GJjNbQkp-nBcOgrsskwDr7WTnUPCGwg3zs,603
2
2
  cinchdb/__main__.py,sha256=OpkDqn9zkTZhhYgvv_grswWLAHKbmxs4M-8C6Z5HfWY,85
3
- cinchdb/config.py,sha256=gocjMnYKLWhgvnteo6zprgwtK6Oevoxq547J_v-C9Ns,5265
3
+ cinchdb/config.py,sha256=hYcT91mMcV0NEvJMAgtJkihfaxHJ97qqGOetnlaNZjI,4889
4
4
  cinchdb/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- cinchdb/cli/main.py,sha256=G26O3EqOaKwOfwkOXOfp2XgyS5fslMgKidOAgW5k6qU,4764
5
+ cinchdb/cli/main.py,sha256=x2bF19e0lY_0V3_tx6CO9M3AaFlII75bAdWaOg0ne_A,4587
6
6
  cinchdb/cli/utils.py,sha256=InAGuo7uENvsfBQu6EKIbbiNXqmvg-BecLmhLvOz5qg,6485
7
- cinchdb/cli/commands/__init__.py,sha256=IRUIPHgdpF4hBDbDy0SgaWn39o5GNUjH54_C42sjlQg,273
7
+ cinchdb/cli/commands/__init__.py,sha256=Wjj_cmfscb_mpC-yFXxWW0-pJoBlI-Vpunb37SARsTs,291
8
8
  cinchdb/cli/commands/branch.py,sha256=Nz8YQYJ7lizSXEAv0usTx85TDOC-N5Ul9KIxN8JQtKc,17973
9
9
  cinchdb/cli/commands/codegen.py,sha256=WsRWmXNTDuaLPyECW5psXM9zOQnKHpUiv8BJnBAjMII,6189
10
10
  cinchdb/cli/commands/column.py,sha256=nUVgjYbxSobJR9CC-qdOvrCl1TgFySr-ctQb0djKZtQ,11676
11
+ cinchdb/cli/commands/data.py,sha256=tRXVeYCShVp5VKJ--Q_XzTokPE6G0gaHSad48iFHa3E,12662
11
12
  cinchdb/cli/commands/database.py,sha256=A3ew8Z987a25dRxmY5XeoB-Ka6SXLKtWogj3XAKUNZE,7332
12
- cinchdb/cli/commands/index.py,sha256=P0sM9lu1rVliacYl49LPqtxNYdrwzKzpVKW10jC-5i4,6096
13
+ cinchdb/cli/commands/index.py,sha256=qL3-UOk_cBLBNR4qK2uPTyFI7qAMxQSE7YtXFXNeSdg,6094
13
14
  cinchdb/cli/commands/query.py,sha256=XW_YL6M5IYHHHMpVB5p-M01kawFxwDOK5B5hGIy_BA8,5044
14
15
  cinchdb/cli/commands/remote.py,sha256=i07hfiAxgrROB9lVJVaKK_nWxT1SGiSbtFb4jvEwxEo,4445
15
16
  cinchdb/cli/commands/table.py,sha256=K233WOV102-g6QlHhL97FhfiGz67zIctbxZblAjY4Lg,10516
16
- cinchdb/cli/commands/tenant.py,sha256=cOWs9Gf13ZE4KTKJZ_AvDwFDdXBUn5i-Av4MoUsc8Go,6199
17
+ cinchdb/cli/commands/tenant.py,sha256=snpMc9Jk2uGmEJCKP39zD26BZLYBhiRxP2M5oGyz0rQ,8309
17
18
  cinchdb/cli/commands/view.py,sha256=ZmS1IW7idzzHAXmgVyY3C4IQRo7toHb6fHNFY_tQJjI,6385
18
19
  cinchdb/cli/handlers/__init__.py,sha256=f2f-Cc96rSBLbVsiIbf-b4pZCKZoHfmhNEvnZ0OurRs,131
19
20
  cinchdb/cli/handlers/codegen_handler.py,sha256=i5we_AbiUW3zfO6pIKWxvtO8OvOqz3H__4xPmTLEuQM,6524
20
21
  cinchdb/core/__init__.py,sha256=iNlT0iO9cM0HLoYwzBavUBoXRh1Tcnz1l_vfbwVxK_Q,246
21
- cinchdb/core/connection.py,sha256=x_xMJv1DiZtWjBoSa2rJSGHs_m6pbtk2JYIYTdeWMtE,5491
22
- cinchdb/core/database.py,sha256=vzVr6Y6iydf9kOU0OO7yVYoHQJ3zlfwAWF2LylJUV0I,27162
22
+ cinchdb/core/connection.py,sha256=_Yt0ki4IbNQJ6lwvloDZ58CCA7SBJwHaB1SU1Yy-rpI,5186
23
+ cinchdb/core/database.py,sha256=4jZwJE4UMpPv77wyQbn0FaTB1MMJv0BHl3umLJzO0q4,33680
23
24
  cinchdb/core/initializer.py,sha256=CAzq947plgEF-KXV-PD-ycJ8Zy4zXCQqCrmQ0-pV0i4,14474
24
- cinchdb/core/maintenance.py,sha256=PAgrSL7Cj9p3rKHV0h_L7gupN6nLD0-5eQpJZNiqyEs,2097
25
- cinchdb/core/path_utils.py,sha256=OPT5WGx6CPdjZbvNsB5fjJGKsl5RtW8-IFacE4TWSxA,4884
26
- cinchdb/infrastructure/metadata_connection_pool.py,sha256=oq4z2B43_kEPUA0QVZz6AI_xl4adOuOSJ7VeZ7ecmP4,4899
27
- cinchdb/infrastructure/metadata_db.py,sha256=QLVHtvto9c2yJw7022JXwfaEilS-k-OICsbHQvA6QSQ,15314
25
+ cinchdb/core/maintenance_utils.py,sha256=FYEZK2FybghuGxo37QeTd1DLsuId0p3rKGIlpPTFJZw,1852
26
+ cinchdb/core/path_utils.py,sha256=xcclmafzETDtivr-Qoi2gP6xZh5C49EoR6duc6YY4gw,4698
27
+ cinchdb/core/tenant_activation.py,sha256=56FaxgZJ2IGYcF6arJfr0gc63wBpyQ4E9ssJDghlPmA,7290
28
+ cinchdb/infrastructure/metadata_connection_pool.py,sha256=PeVsyuZKdI3Y7_InWMOn9HcSNhE0MVcP7I7wAj9esmI,4859
29
+ cinchdb/infrastructure/metadata_db.py,sha256=gP-UORs2Ec33p9_64L2xPTZ51PQDYrmfHlofJfQUM2M,19933
28
30
  cinchdb/managers/__init__.py,sha256=ic61ZUdsg-muq0ETYO6fuZRQWF4j7l920PthTkt2QrE,808
29
- cinchdb/managers/branch.py,sha256=ctX2RpodtQ-fLMpGzHyJMydseb0rK4gLjMz2ix82seU,9455
30
- cinchdb/managers/change_applier.py,sha256=cCgjUL6SJvrgVCCHAw0mAbGqZqKmMsLa0QbziSiuW68,15576
31
+ cinchdb/managers/branch.py,sha256=bEzl83mN9a9KW8AHC_w50QHtVdVPHUzEY-j9T2EAyR4,9445
32
+ cinchdb/managers/change_applier.py,sha256=JWfK-B42fCc5ANGvIIogzF5SeSCkSO16y67RCNsPu08,15602
31
33
  cinchdb/managers/change_comparator.py,sha256=08pwybpSt36cFwhZRSIkHynvFMUaLKEVwa8Ajn_R9yQ,6862
32
34
  cinchdb/managers/change_tracker.py,sha256=U93BPnuGv8xSaO5qr_y5Q8ppKrVXygozdp5zUvLUqwg,5054
33
35
  cinchdb/managers/codegen.py,sha256=1CfIwjgHnNDdjrq4SzQ9VE7DFgnWfk7RtpupBFUTqxk,21804
34
- cinchdb/managers/column.py,sha256=i0EzDKavMvZeeaVrY9OVRNHOW60v0rUEkDjMtIs3PaE,20749
35
- cinchdb/managers/data.py,sha256=zS1HkMGf436m6f8VdFAqQbQFgo4sL5yKJRcRf4A6lIc,16253
36
- cinchdb/managers/index.py,sha256=n9bCXggZP6muJQZXCpTT46JvuvcbbnYgeV3j6iXtTVM,10371
36
+ cinchdb/managers/column.py,sha256=JyBIu372ExMppvqklhtVAc-eZzikIqedB_8uoJtFu-Y,20755
37
+ cinchdb/managers/data.py,sha256=zAtVLR4GaiY7ijIbWAXC7JN701KtX2zgcg4ZUlGpyts,23176
38
+ cinchdb/managers/index.py,sha256=55_fafWd4lSju4Nw32B_1Fi1c2DPHxOgAhdsqcTR404,10343
37
39
  cinchdb/managers/merge_manager.py,sha256=R8S2hLkLJg4hLDpeJTzjVkduZgqPOjXtYgOSJhTXXrE,15690
38
- cinchdb/managers/query.py,sha256=mBWsWjdAqWDzuvxKsY49onAOP6dsZOMhVKwA7Y1kunE,8617
39
- cinchdb/managers/table.py,sha256=8w6L4-DJgvsvpTYDW123Y9JuXO95O-SfOs-XMqWSvzA,14028
40
- cinchdb/managers/tenant.py,sha256=szmgErdqs3KXPi5LugMnUR76PxWARMj6H6y-JvcYd68,33965
41
- cinchdb/managers/view.py,sha256=v9gYtRufZyxywPKLGvIjvlUXcxYh9CLRArefu9QX6zk,7809
40
+ cinchdb/managers/query.py,sha256=hgNHda20PWMijbVRnjTKFGFBvBthep2fulkJEK1gkqU,8562
41
+ cinchdb/managers/table.py,sha256=mv5p5WCXy1VXKRyaAZquZofdKR0gEth7It82j6p7j0E,15252
42
+ cinchdb/managers/tenant.py,sha256=gk3Nn68n4avl2mcMcPB5HuKOsOPsMhnvgxHyK8Uwuik,31634
43
+ cinchdb/managers/view.py,sha256=uXcRnZxF4TCcycbwH7cTje_QU3NO47fmOzJULQHHeVI,7815
42
44
  cinchdb/models/__init__.py,sha256=cZ-ailJ6qu44Iap5Rq555iB-_w9ufXVDBH3rDH-ojk0,622
43
45
  cinchdb/models/base.py,sha256=7j4rlFTP5K9ZuF8vxwC7lMFEaL7O90NJ47Ig5i7ubcw,1320
44
46
  cinchdb/models/branch.py,sha256=gRgLpRFkMC3fxf9ZigVOkS6wdkBERWqlLk0_gOYjqNk,1180
@@ -48,13 +50,15 @@ cinchdb/models/project.py,sha256=6GMXUZUsEIebqQJgRXIthWzpWKuNNmJ3drgI1vFDrMo,644
48
50
  cinchdb/models/table.py,sha256=nxf__C_YvDOW-6-vdOQ4xKmwWPZwgEdYw1I4mO_C44o,2955
49
51
  cinchdb/models/tenant.py,sha256=wTvGoO_tQdtueVB0faZFVIhSjh_DNJzywflrUpngWTM,1072
50
52
  cinchdb/models/view.py,sha256=q6j-jYzFJuhRJO87rKt6Uv8hOizHQx8xwoPKoH6XnNY,530
51
- cinchdb/security/__init__.py,sha256=GWV6OIczkPhJazLvMFcT54aDUI6_mU9MTZu09TEGrJ0,45
52
- cinchdb/security/encryption.py,sha256=nEmjNot2NyJZivrsf4jYB9Ei-LJdb2_O6BfXsO9-pww,4168
53
+ cinchdb/plugins/__init__.py,sha256=jZtwwY0t_dhFKz-uR2V3KWBmAGknWnoLkiDWSzULdc8,293
54
+ cinchdb/plugins/base.py,sha256=Upfgw78eOhTAfQd9FwcgGZLIT0zefPDgD4LlpzXgOBg,2232
55
+ cinchdb/plugins/decorators.py,sha256=vY9Zh5nhGiSM6kO55srGHSP32gnyMq77UtJSO3lULq4,1550
56
+ cinchdb/plugins/manager.py,sha256=vetdsiwTkadp3BrhjUIp2vkmCpqfuXqy-3yQBdlxruQ,8143
53
57
  cinchdb/utils/__init__.py,sha256=yQQhEjndDiB2SUJybUmp9dvEOQKiR-GySe-WiCius5E,490
54
58
  cinchdb/utils/name_validator.py,sha256=dyGX5bjlTFRA9EGrWRQKp6kR__HSV04hLV5VueJs4IQ,4027
55
59
  cinchdb/utils/sql_validator.py,sha256=aWOGlPX0gBkuR6R1EBP2stbP4PHZuI6FUBi2Ljx7JUI,5815
56
- cinchdb-0.1.14.dist-info/METADATA,sha256=uNJzcdK2TKRlTyob3Kpf9mJQldTrxw-nDJcqOiz9wAo,6116
57
- cinchdb-0.1.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
58
- cinchdb-0.1.14.dist-info/entry_points.txt,sha256=VBOIzvnGbkKudMCCmNORS3885QSyjZUVKJQ-Syqa62w,47
59
- cinchdb-0.1.14.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
60
- cinchdb-0.1.14.dist-info/RECORD,,
60
+ cinchdb-0.1.17.dist-info/METADATA,sha256=cTeJ-n2bL2mqALrAZt9yPES_Xt6uC_suDB8HJmNTFTo,6155
61
+ cinchdb-0.1.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
+ cinchdb-0.1.17.dist-info/entry_points.txt,sha256=VBOIzvnGbkKudMCCmNORS3885QSyjZUVKJQ-Syqa62w,47
63
+ cinchdb-0.1.17.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
64
+ cinchdb-0.1.17.dist-info/RECORD,,
@@ -1,73 +0,0 @@
1
- """Maintenance mode utilities for CinchDB."""
2
-
3
- from pathlib import Path
4
- import json
5
- from typing import Dict, Any, Optional
6
-
7
- from cinchdb.core.path_utils import get_branch_path
8
-
9
-
10
- class MaintenanceError(Exception):
11
- """Exception raised when operation blocked by maintenance mode."""
12
-
13
- pass
14
-
15
-
16
- def is_branch_in_maintenance(project_root: Path, database: str, branch: str) -> bool:
17
- """Check if a branch is in maintenance mode.
18
-
19
- Args:
20
- project_root: Path to project root
21
- database: Database name
22
- branch: Branch name
23
-
24
- Returns:
25
- True if in maintenance mode, False otherwise
26
- """
27
- branch_path = get_branch_path(project_root, database, branch)
28
- maintenance_file = branch_path / ".maintenance_mode"
29
- return maintenance_file.exists()
30
-
31
-
32
- def get_maintenance_info(
33
- project_root: Path, database: str, branch: str
34
- ) -> Optional[Dict[str, Any]]:
35
- """Get maintenance mode information if active.
36
-
37
- Args:
38
- project_root: Path to project root
39
- database: Database name
40
- branch: Branch name
41
-
42
- Returns:
43
- Maintenance info dict or None if not in maintenance
44
- """
45
- branch_path = get_branch_path(project_root, database, branch)
46
- maintenance_file = branch_path / ".maintenance_mode"
47
-
48
- if maintenance_file.exists():
49
- with open(maintenance_file, "r") as f:
50
- return json.load(f)
51
-
52
- return None
53
-
54
-
55
- def check_maintenance_mode(project_root: Path, database: str, branch: str) -> None:
56
- """Check maintenance mode and raise error if active.
57
-
58
- Args:
59
- project_root: Path to project root
60
- database: Database name
61
- branch: Branch name
62
-
63
- Raises:
64
- MaintenanceError: If branch is in maintenance mode
65
- """
66
- if is_branch_in_maintenance(project_root, database, branch):
67
- info = get_maintenance_info(project_root, database, branch)
68
- reason = (
69
- info.get("reason", "Maintenance in progress")
70
- if info
71
- else "Maintenance in progress"
72
- )
73
- raise MaintenanceError(f"Branch '{branch}' is in maintenance mode: {reason}")
@@ -1 +0,0 @@
1
- """Security module for CinchDB encryption."""