Python Backends¶
Python-defined databases implement AbstractDatabase. AbstractDatabaseFileSystem wraps
that object and delegates filesystem behavior through the Rust DatabaseFs bridge, so Python
backend authors do not reimplement path parsing.
Minimal Backend¶
import pyarrow as pa
from fsspec_db import (
AbstractDatabase,
AbstractDatabaseFileSystem,
ColumnInfo,
ConstraintInfo,
IndexInfo,
RelationInfo,
SchemaInfo,
)
class MemoryDatabase(AbstractDatabase):
def __init__(self):
self.table = pa.table({"id": [1], "name": ["ada"]})
def dialect(self):
return "sqlite"
def list_schemas(self):
return [SchemaInfo("main")]
def list_relations(self, schema):
return [RelationInfo("users", "table", row_count=self.table.num_rows)]
def list_columns(self, schema, relation):
return [
ColumnInfo("id", "INTEGER", False, None, 1, True),
ColumnInfo("name", "TEXT", True, None, 2),
]
def list_indexes(self, schema, relation):
return []
def list_constraints(self, schema, relation):
return [ConstraintInfo("pk_users", "pk", ["id"])]
def relation_info(self, schema, relation):
return self.list_relations(schema)[0]
def view_definition(self, schema, view):
raise FileNotFoundError(view)
def query(self, sql, params=None):
return self.table
def insert(self, schema, relation, table, mode="append"):
self.table = table if mode == "truncate" else pa.concat_tables([self.table, table])
return table.num_rows
fs = AbstractDatabaseFileSystem(MemoryDatabase())
Required Methods¶
Method |
Purpose |
|---|---|
|
Returns a dialect name such as |
|
Returns |
|
Returns tables and views as |
|
Returns |
|
Returns |
|
Returns |
|
Returns one relation or raises |
|
Returns SQL text for a view. |
|
Returns a |
|
Inserts a |
Raise normal Python filesystem-style exceptions where possible. The bridge maps
FileNotFoundError, PermissionError, FileExistsError, NotADirectoryError,
IsADirectoryError, and ValueError back into fsspec exceptions.
Query And Insert Boundaries¶
AbstractDatabaseFileSystem.query() and open_query() both cross the Rust bridge. Query
parameters are marshaled to Rust values, then back into Python for the backend’s query
method. insert() receives a pyarrow.Table decoded from the incoming relation data file.
This keeps Python-defined backends behaviorally aligned with native Rust backends:
fs.cat_file("/main/users.arrow")
fs.pipe_file("/main/users.arrow", arrow_ipc_bytes)
fs.query("SELECT * FROM users")
When To Use This¶
Use Python-defined backends when a database driver already exists in Python, when you want to adapt SQLAlchemy/DBAPI metadata, or when the “database” is a virtual table over another source. Native Rust backends are better for production driver integrations where avoiding the GIL and reducing Python boundary crossings matter.