API reference¶
Auto-generated from source docstrings. Re-regenerated on every docs build.
bqemulator¶
bqemulator — a local emulator for Google BigQuery.
This is the root package. Public re-exports here are intentionally minimal; consumers should import from dedicated submodules.
Typical uses
from bqemulator import version from bqemulator.server import EmulatorServer from bqemulator.config import Settings
bqemulator.config¶
bqemulator.config ¶
Settings — the single source of runtime configuration.
Configuration sources in priority order (high to low):
- Explicit constructor kwargs (set by CLI flags in :mod:
bqemulator.cli). - Environment variables prefixed
BQEMU_. .bqemu.tomlfile in the current working directory.- Built-in defaults.
The settings object is constructed once at startup by the composition root
(:mod:bqemulator.server) and injected into every subsystem that needs it.
Never access settings via a module-level global.
PersistenceMode ¶
Bases: StrEnum
How the emulator persists its DuckDB data.
Source code in src/bqemulator/config.py
LogLevel ¶
LogFormat ¶
Settings ¶
Bases: BaseSettings
Runtime settings for bqemulator.
Attributes map 1:1 to BQEMU_* env vars via the prefix and to CLI
flags via the :mod:bqemulator.cli definitions.
Source code in src/bqemulator/config.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | |
duckdb_path ¶
Return the DuckDB connection string for this configuration.
':memory:' for ephemeral, otherwise the file path under
data_dir. Raises :class:ValueError if data_dir is required
but missing.
Source code in src/bqemulator/config.py
bqemulator.domain.errors¶
bqemulator.domain.errors ¶
Domain-error hierarchy.
Every expected error in the emulator inherits from :class:DomainError and
maps to the exact JSON shape the real BigQuery service returns via
:meth:DomainError.to_bigquery_error.
Reference: https://cloud.google.com/bigquery/docs/error-messages
The HTTP status code, BigQuery reason string, and gRPC canonical
status code are attached at the subclass level and used by adapter layers
to render responses consistently.
ErrorDetail
dataclass
¶
A single error entry in BigQuery's ErrorProto.errors array.
Source code in src/bqemulator/domain/errors.py
to_dict ¶
Render as a dict matching BigQuery's JSON ErrorProto shape.
Source code in src/bqemulator/domain/errors.py
DomainError ¶
Bases: Exception
Base class for all expected domain errors.
Subclasses define three class variables that adapters use to render the error:
http_status— HTTP status code for REST responses.bq_reason— BigQueryreasonstring. Matches the values the real service returns inErrorProto.reason.grpc_status_name— canonical gRPC status name (INVALID_ARGUMENT,NOT_FOUND, etc.).
Source code in src/bqemulator/domain/errors.py
to_bigquery_error ¶
Render as BigQuery's JSON error shape.
Matches::
{
"error": {
"code": 400,
"message": "...",
"errors": [{"domain": "global", "reason": "...", "message": "..."}],
"status": "INVALID_ARGUMENT",
}
}
Source code in src/bqemulator/domain/errors.py
InvalidQueryError ¶
Bases: DomainError
Malformed SQL, unknown function, or semantic analysis failure.
Source code in src/bqemulator/domain/errors.py
ValidationError ¶
Bases: DomainError
Request failed schema or semantic validation (non-SQL).
Source code in src/bqemulator/domain/errors.py
NotFoundError ¶
Bases: DomainError
A resource (dataset, table, job, routine, model) was not found.
Source code in src/bqemulator/domain/errors.py
AlreadyExistsError ¶
Bases: DomainError
Create request targeted a resource that already exists.
Source code in src/bqemulator/domain/errors.py
PermissionDeniedError ¶
Bases: DomainError
Row-access policy, authorized view, or other policy check failed.
The emulator does not enforce IAM, but it does enforce row-access policies on configured tables. This is raised when a query would return rows the caller is not permitted to see.
Source code in src/bqemulator/domain/errors.py
QuotaExceededError ¶
Bases: DomainError
A configurable emulator quota was exceeded (e.g. max concurrent jobs).
Source code in src/bqemulator/domain/errors.py
UnsupportedFeatureError ¶
Bases: DomainError
A feature explicitly out of scope for v1 was invoked.
Raised for BigQuery ML statements, scheduled queries, Data Transfer
Service operations, and any other feature enumerated in
docs/reference/out-of-scope.md.
Source code in src/bqemulator/domain/errors.py
InternalError ¶
Bases: DomainError
Unexpected condition that should never happen in a healthy build.
Source code in src/bqemulator/domain/errors.py
OutOfRangeError ¶
Bases: DomainError
A requested position/time fell outside the valid range.
BigQuery returns outOfRange when FOR SYSTEM_TIME AS OF is
called with a timestamp outside the time-travel retention window or
before the table existed.
Source code in src/bqemulator/domain/errors.py
ResourceRef
dataclass
¶
Reference to a BigQuery-style resource (for error messages).
Source code in src/bqemulator/domain/errors.py
format ¶
Human-readable project.dataset.resource form.
Source code in src/bqemulator/domain/errors.py
resource_not_found ¶
Helper to raise a consistent 'not found' error for any resource.
Source code in src/bqemulator/domain/errors.py
resource_already_exists ¶
Helper to raise a consistent 'already exists' error for any resource.
Source code in src/bqemulator/domain/errors.py
bqemulator.domain.result¶
bqemulator.domain.result ¶
Result type — explicit success/failure for expected domain outcomes.
Exceptions are reserved for unexpected failures. For expected outcomes
(SQL parse error, catalog miss, validation failure), use :class:Result.
Example::
def translate(sql: str) -> Result[str, InvalidQueryError]:
try:
return Ok(_translate(sql))
except sqlglot.errors.ParseError as exc:
return Err(InvalidQueryError(str(exc)))
match translate(user_sql):
case Ok(duckdb_sql):
...
case Err(error):
...
Result
module-attribute
¶
A disjoint union of :class:Ok and :class:Err.
Use with Python 3.11+ match statements for exhaustive handling.
Ok
dataclass
¶
Bases: Generic[T]
Success case of :class:Result.
Source code in src/bqemulator/domain/result.py
Err
dataclass
¶
Bases: Generic[E]
Failure case of :class:Result.
Source code in src/bqemulator/domain/result.py
bqemulator.domain.ids¶
bqemulator.domain.ids ¶
Typed identifiers for BigQuery resources.
BigQuery's identifier rules (from the docs):
project_id: 6-30 characters; lowercase letters, digits, hyphens; must start with a letter and not end with a hyphen.dataset_id: up to 1024 characters; letters (any case), digits, underscores. No hyphens or dots.table_id: up to 1024 characters; same character set as dataset, plus hyphens and some Unicode letters in certain contexts.job_id: up to 1024 characters; letters, digits, dashes, underscores.routine_id: same as dataset.
We model each as a frozen dataclass with a validating constructor.
The raw string is exposed via the value attribute; equality and hashing
work as expected.
ProjectId
dataclass
¶
A validated BigQuery project id.
Source code in src/bqemulator/domain/ids.py
DatasetId
dataclass
¶
A validated BigQuery dataset id (without project qualification).
Source code in src/bqemulator/domain/ids.py
TableId
dataclass
¶
A validated BigQuery table id (without dataset qualification).
Source code in src/bqemulator/domain/ids.py
JobId
dataclass
¶
A validated BigQuery job id.
Source code in src/bqemulator/domain/ids.py
RoutineId
dataclass
¶
A validated BigQuery routine id (without dataset qualification).
Source code in src/bqemulator/domain/ids.py
validate_project_id ¶
validate_dataset_id ¶
validate_table_id ¶
validate_routine_id ¶
validate_job_id ¶
validate_table_ref ¶
Validate a (project, dataset, table) triple in one call.
Source code in src/bqemulator/domain/ids.py
bqemulator.domain.clock¶
bqemulator.domain.clock ¶
Clock protocol — injectable time source for deterministic tests.
Production code uses :class:SystemClock. Tests inject :class:FrozenClock
to make timestamps predictable.
Every timestamp emitted by the emulator (job start/end, table creation, row
insert time) flows through a :class:Clock; no code should call
:func:datetime.now directly outside of this module.
Clock ¶
Bases: Protocol
Protocol for injectable time sources.
Source code in src/bqemulator/domain/clock.py
now ¶
SystemClock ¶
FrozenClock
dataclass
¶
Test clock that advances only when :meth:advance is called.
Example::
clock = FrozenClock(datetime(2026, 4, 15, tzinfo=UTC))
clock.now() # datetime(2026, 4, 15, 0, 0, tzinfo=UTC)
clock.advance(seconds=60)
clock.now() # datetime(2026, 4, 15, 0, 1, tzinfo=UTC)
Source code in src/bqemulator/domain/clock.py
now ¶
now_ms ¶
advance ¶
advance(
*,
seconds: float = 0,
milliseconds: float = 0,
minutes: float = 0,
hours: float = 0,
days: float = 0,
) -> None
Advance the clock by the given amount.
Source code in src/bqemulator/domain/clock.py
bqemulator.domain.events¶
bqemulator.domain.events ¶
Internal domain events.
Events are emitted when observable state changes. They power:
- Materialized view auto-refresh (base-table DML invalidates cached MVs).
- Query result cache invalidation.
- Structured audit logging for debugging.
Events are consumed in-process via a simple synchronous bus. No persistent event log; events never cross process boundaries.
DomainEvent
dataclass
¶
DatasetCreated
dataclass
¶
DatasetDeleted
dataclass
¶
TableCreated
dataclass
¶
TableSchemaChanged
dataclass
¶
Bases: DomainEvent
A table's schema (columns, modes) has changed.
Source code in src/bqemulator/domain/events.py
TableDataChanged
dataclass
¶
Bases: DomainEvent
Rows in a table have been inserted, updated, or deleted.
Triggers query-cache invalidation for anything that depends on the table and materialized-view refresh for dependent MVs.
Source code in src/bqemulator/domain/events.py
TableDeleted
dataclass
¶
JobStarted
dataclass
¶
JobCompleted
dataclass
¶
Bases: DomainEvent
A job has transitioned to DONE (success or failure).
Source code in src/bqemulator/domain/events.py
EventBus ¶
Synchronous, type-dispatched event bus.
Intentionally minimal — no persistence, no async, no ordering guarantees beyond registration order. Adequate for in-process fan-out.
Usage::
bus = EventBus()
bus.subscribe(TableDataChanged, invalidate_query_cache)
bus.publish(TableDataChanged("proj", "sales", "orders"))
Source code in src/bqemulator/domain/events.py
subscribe ¶
Register handler to be called for every event of event_type.
unsubscribe ¶
Remove a previously registered handler. Silently skip if absent.
Source code in src/bqemulator/domain/events.py
publish ¶
bqemulator.storage.engine¶
bqemulator.storage.engine ¶
DuckDB engine — single-writer connection with async-safe lifecycle.
The emulator uses exactly one :class:duckdb.DuckDBPyConnection for the
entire process. Writes serialize on an :class:asyncio.Lock; reads do not
take the lock (DuckDB provides internal read/write concurrency for the
same connection object).
The engine also handles startup tasks:
- Ensure the reserved
_bqemulator_catalogschema exists. - Set the connection's time zone to
UTC(BigQuery TIMESTAMP semantics). - Install and load the
spatialextension. Required — startup fails fast with a clear error if the extension cannot be installed/loaded (e.g. offline build with no cached extension), because GEOGRAPHY queries depend on it.
DuckDBEngine ¶
Async-friendly wrapper around a single DuckDB connection.
Usage::
engine = DuckDBEngine(settings)
await engine.start()
async with engine.write_lock():
engine.execute("INSERT INTO ...")
await engine.stop()
:class:DuckDBEngine is intentionally synchronous-under-the-hood. DuckDB
releases the GIL during query execution, so awaiting the write lock
gives other tasks a chance to progress. Long queries should be run
inside asyncio.to_thread by the caller.
Source code in src/bqemulator/storage/engine.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | |
connection
property
¶
Return the underlying DuckDB connection.
Raises :class:InternalError if :meth:start has not been called.
start
async
¶
Open the DuckDB connection and run startup hooks.
Source code in src/bqemulator/storage/engine.py
stop
async
¶
Close the DuckDB connection (idempotent).
Source code in src/bqemulator/storage/engine.py
execute ¶
Execute a SQL statement. Returns the cursor-like connection.
Source code in src/bqemulator/storage/engine.py
fetch_arrow ¶
Execute and fetch results as a pyarrow.Table.
Uses to_arrow_table() (DuckDB >=1.4) with a fallback to the
deprecated fetch_arrow_table() for older builds.
Annotates each field with the original DuckDB column type as
bqemu.duckdb_type metadata. DuckDB's JSON / DECIMAL /
TIMESTAMP_TZ etc. flatten to their underlying physical Arrow
type (string / int64 / …) at conversion time, so the
REST schema renderer has no way to distinguish a JSON-typed
column from a regular VARCHAR after the fact. Preserving the
DuckDB-side type as field metadata lets
:func:bqemulator.jobs.executor._arrow_field_to_schema_entry
recover the BigQuery wire-format type for those columns.
Source code in src/bqemulator/storage/engine.py
write_lock
async
¶
Acquire the exclusive write lock for this engine.
All DDL and DML must be wrapped in async with engine.write_lock().
Concurrent readers may proceed without the lock.
Source code in src/bqemulator/storage/engine.py
bqemulator.catalog.repository¶
bqemulator.catalog.repository ¶
CatalogRepository protocol.
Any implementation must honor the contracts documented on each method:
get_*returnsNonewhen the resource does not exist. The API adapter is responsible for convertingNoneto :class:NotFoundError.create_*raises :class:AlreadyExistsErrorwhen a resource with the same identity already exists.update_*raises :class:NotFoundErrorwhen the resource is absent.delete_*is idempotent when called withnot_found_ok=True; it otherwise raises :class:NotFoundError.list_*returns an empty tuple when no resources match; neverNone.
Implementations must be safe to call from a single asyncio task at a time. Concurrent writes should be gated by the caller (the storage engine's write lock) — the repository itself does not serialize writes.
CatalogRepository ¶
Bases: Protocol
Repository protocol for BigQuery-style metadata.
Source code in src/bqemulator/catalog/repository.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | |
list_datasets ¶
list_all_datasets ¶
Return every dataset across every project (possibly empty).
Used by the admin / export / seed paths that need a full catalog walk without knowing the project ids in advance. Order is implementation-defined.
Source code in src/bqemulator/catalog/repository.py
get_dataset ¶
create_dataset ¶
update_dataset ¶
delete_dataset ¶
delete_dataset(
project_id: str,
dataset_id: str,
*,
not_found_ok: bool = False,
delete_contents: bool = False,
) -> None
Delete a dataset. delete_contents cascades to tables/routines.
list_tables ¶
list_storage_tables ¶
Return table IDs physically present in storage for this dataset.
Unlike :meth:list_tables (which returns BigQuery-level
:class:TableMeta for catalog-registered tables only), this
method also surfaces tables created directly via SQL DDL
(CREATE TABLE … AS SELECT). The wildcard-table expander
uses it so wildcard references engage on DDL-created shards
the catalog cache hasn't been notified about.
Returns table IDs without metadata; order is implementation-defined. Returns an empty tuple if no tables exist in the dataset.
Source code in src/bqemulator/catalog/repository.py
get_table ¶
create_table ¶
update_table ¶
delete_table ¶
list_views ¶
Return all VIEW-typed tables in the dataset (possibly empty).
Backs INFORMATION_SCHEMA.VIEWS. The returned
:class:TableMeta instances carry table_type='VIEW' and
view_query populated with the BigQuery SQL view definition.
Source code in src/bqemulator/catalog/repository.py
list_partitions ¶
Return the distinct partitions for a (possibly partitioned) table.
For a time-partitioned table this enumerates the distinct
partition-grain values (YYYYMMDD for DAY, YYYYMMDDHH for
HOUR, etc.) plus row counts per partition. For an integer-range
partitioned table the bucket starts are stringified
("0", "100", …). For an unpartitioned table the entire
table is treated as a single partition with
partition_id='__NULL__' (BigQuery's documented sentinel).
Implementations that have a live DuckDB engine query the physical storage; in-memory unit tests without a wired engine return an empty tuple.
Source code in src/bqemulator/catalog/repository.py
list_routines ¶
get_routine ¶
create_routine ¶
update_routine ¶
delete_routine ¶
list_jobs ¶
get_job ¶
upsert_job ¶
delete_job ¶
Delete a job record (metadata only; job results handled separately).
list_snapshots_for_table ¶
list_snapshots_for_table(
project_id: str, dataset_id: str, table_id: str
) -> tuple[SnapshotMeta, ...]
Return all snapshots for a base table ordered by snapshot_time.
list_all_snapshots ¶
create_snapshot ¶
delete_snapshot ¶
list_materialized_views ¶
list_all_materialized_views ¶
get_materialized_view ¶
upsert_materialized_view ¶
delete_materialized_view ¶
list_row_access_policies ¶
list_row_access_policies(
project_id: str, dataset_id: str, table_id: str
) -> tuple[RowAccessPolicyMeta, ...]
Return all row access policies on the table (possibly empty).
list_all_row_access_policies ¶
get_row_access_policy ¶
create_row_access_policy ¶
Insert a new row access policy. Raises AlreadyExistsError on conflict.
update_row_access_policy ¶
Replace an existing row access policy. Raises NotFoundError if absent.
delete_row_access_policy ¶
delete_row_access_policy(
project_id: str,
dataset_id: str,
table_id: str,
policy_id: str,
*,
not_found_ok: bool = False,
) -> None
Delete a row access policy.
bqemulator.catalog.models¶
bqemulator.catalog.models ¶
Frozen Pydantic models for catalog entities.
These models are the single in-memory shape for metadata. They are serialized to JSON for storage (in DuckDB catalog tables) and to REST responses.
Models are frozen=True — any mutation must use .model_copy(update=...)
which produces a new instance. This is cheap (shallow copy) and makes the
catalog thread/task-safe by construction.
TableFieldSchema ¶
Bases: _Frozen
A single column in a BigQuery table schema.
Matches the TableFieldSchema REST resource.
Source code in src/bqemulator/catalog/models.py
TableSchema ¶
TimePartitioning ¶
Bases: _Frozen
Time-unit partitioning configuration.
Source code in src/bqemulator/catalog/models.py
RangePartitioning ¶
Clustering ¶
AccessEntry ¶
Bases: _Frozen
A single entry in a dataset's access array.
Mirrors the BigQuery REST Dataset.access shape. The fields are
mutually exclusive — exactly one of role + (user_by_email |
group_by_email | domain | special_group | iam_member), view,
dataset, or routine should be populated. The model does
not enforce mutual exclusion (the REST adapter does), so callers
can deserialize a heterogeneous array uniformly.
Source code in src/bqemulator/catalog/models.py
DatasetMeta ¶
Bases: _Frozen
Metadata for a BigQuery dataset.
Source code in src/bqemulator/catalog/models.py
TableMeta ¶
Bases: _Frozen
Metadata for a BigQuery table, view, or related entity.
Source code in src/bqemulator/catalog/models.py
SnapshotMeta ¶
Bases: _Frozen
Metadata for a captured snapshot.
AUTO snapshots power FOR SYSTEM_TIME AS OF time travel. They
live in the reserved _bqemulator_snapshots DuckDB schema and
expire after the configured retention window.
USER snapshots back CREATE SNAPSHOT TABLE statements. They
live in the regular project__dataset schema, appear in the
tables catalog with table_type=SNAPSHOT, and never expire
under the retention policy — they are only removed by an explicit
DROP SNAPSHOT TABLE.
Source code in src/bqemulator/catalog/models.py
MaterializedViewMeta ¶
Bases: _Frozen
Metadata for a materialized view.
The view's physical rows live in a regular DuckDB table in the
dataset's schema, so TableMeta still carries the schema and
identity. MaterializedViewMeta captures the additional data
the refresh subsystem needs: the BigQuery source query, its base
table dependencies, staleness, and refresh bookkeeping.
Source code in src/bqemulator/catalog/models.py
PartitionMeta ¶
Bases: _Frozen
Derived metadata for a single partition slice of a partitioned table.
Not a persisted catalog entity — synthesised on demand from the
underlying DuckDB rows when INFORMATION_SCHEMA.PARTITIONS is
queried. partition_id follows BigQuery's documented format:
- DAY-partitioned:
"YYYYMMDD"(e.g."20260520"). - HOUR-partitioned:
"YYYYMMDDHH". - MONTH-partitioned:
"YYYYMM". - YEAR-partitioned:
"YYYY". - Integer-range partitioned: stringified bucket start (e.g.
"100"). - Unpartitioned tables:
"__NULL__"(BigQuery's documented sentinel).
Source code in src/bqemulator/catalog/models.py
RowAccessPolicyMeta ¶
Bases: _Frozen
Metadata for a BigQuery row access policy.
A row access policy restricts a SELECT against project.dataset.table
to the rows for which filter_predicate evaluates to TRUE and
where the caller's IAM-member identity matches one of grantees.
See ADR 0018 for the enforcement model and matching rules.
Source code in src/bqemulator/catalog/models.py
RoutineArgument ¶
Bases: _Frozen
A single argument to a routine.
Source code in src/bqemulator/catalog/models.py
RoutineMeta ¶
Bases: _Frozen
Metadata for a BigQuery routine (UDF, procedure, or TVF).
Source code in src/bqemulator/catalog/models.py
JobMeta ¶
Bases: _Frozen
Metadata for a BigQuery job.
Source code in src/bqemulator/catalog/models.py
bqemulator.server¶
bqemulator.server ¶
Composition root — wires every subsystem into a running server.
This module is the ONLY place that constructs top-level objects. Every other subsystem takes its collaborators via constructor injection.
Public entry points:
- :func:
run_forever— blocking, used by the CLI'sstartcommand. - :class:
EmulatorServer— programmatic, used by the pytest fixture and for embedding into other Python processes.
EmulatorServer ¶
Programmatic lifecycle for the emulator.
Example::
server = EmulatorServer(Settings())
await server.start()
# ... use the emulator ...
await server.stop()
Or synchronously via :meth:run_forever, used by the CLI.
Source code in src/bqemulator/server.py
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | |
start
async
¶
Start all subsystems. Returns once the servers are accepting traffic.
Source code in src/bqemulator/server.py
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | |
stop
async
¶
Stop all subsystems. Idempotent.
Source code in src/bqemulator/server.py
bqemulator.testing.fixtures¶
bqemulator.testing.fixtures ¶
Pytest fixtures for consumers of bqemulator.
Registered as a pytest plugin via the pytest11 entry point declared in
pyproject.toml. Installing bqemulator automatically makes these
fixtures available — no conftest.py wiring required.
Fixtures¶
bqemu_settings
Session-scoped. Returns a :class:Settings configured for ephemeral
in-memory use on random free ports. Override via indirect
parametrization for per-test tweaks.
bqemu_server
Session-scoped. A running :class:EmulatorServer. Sets the
BIGQUERY_EMULATOR_HOST env var for the session and unsets it on
teardown.
bqemu_endpoint
Session-scoped. {"rest_url": ..., "grpc_endpoint": ...} dict.
bqemu_client
Function-scoped. A configured google.cloud.bigquery.Client pointing
at the emulator. Only available if google-cloud-bigquery is
installed.
EmulatorEndpoint
dataclass
¶
bqemu_settings ¶
Default settings for session-scoped fixtures.
Ephemeral mode, random ports, INFO logging.
Source code in src/bqemulator/testing/fixtures.py
bqemu_server ¶
Start an in-process emulator for the entire test session.
Runs the server on a background asyncio loop to avoid conflicting with test event loops.
Source code in src/bqemulator/testing/fixtures.py
bqemu_endpoint ¶
Session-scoped :class:EmulatorEndpoint.
Source code in src/bqemulator/testing/fixtures.py
bqemu_client ¶
Return a google.cloud.bigquery.Client pointed at the emulator.
Raises :class:pytest.skip.Exception if google-cloud-bigquery is
not installed.
Source code in src/bqemulator/testing/fixtures.py
bqemulator.testing.testcontainers¶
bqemulator.testing.testcontainers ¶
Testcontainers wrapper for the published Docker image.
Use this when:
- You want a real subprocess (matches CI/CD more closely than in-process).
- You are testing clients in languages other than Python.
- You want persistence across test functions.
Example::
with BigQueryEmulatorContainer() as emu:
rest_url = emu.get_rest_url()
grpc_endpoint = emu.get_grpc_endpoint()
# ... run tests ...
BigQueryEmulatorContainer ¶
Bases: DockerContainer
Manages a bqemulator Docker container for testing.
Source code in src/bqemulator/testing/testcontainers.py
start ¶
Start the container and wait until the REST listener is ready.
testcontainers emits a DeprecationWarning when
wait_for_logs is passed a plain string / regex because the
library plans to replace it with a structured wait strategy.
We silence that warning at the call site so consumers of this
wrapper don't need to edit their own pytest filter list.
Source code in src/bqemulator/testing/testcontainers.py
get_rest_url ¶
Return the externally-reachable REST URL.
get_grpc_endpoint ¶
Return the externally-reachable gRPC endpoint (host:port).