Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ All tests must pass before a change is considered complete.

### Building

- Debug build: `./scripts/build.sh`
- Debug build: `./scripts/build.sh -d` (equivalent to `./scripts/build.sh`; debug is the default)
- Release build: `./scripts/build.sh -r`
- Release build with C++ unit test binary: `./scripts/build.sh -r -t`

Expand All @@ -25,6 +25,7 @@ All tests must pass before a change is considered complete.
- Python tests: `./scripts/run_tests.sh`
- Final Python test checks: `./scripts/run_tests.sh -j 6`
- C++ tests after a `-t` build: `./build/release/tests.x`
- Before final handoff, also verify the code in debug mode with a debug build (`./scripts/build.sh -d`) and the relevant Python tests against that debug build. Debug assertions are part of the required validation, not optional diagnostics.
- During development, do not run stress tests by default; they are intentionally slow. Run focused tests specific to the feature or refactor being worked on before finalization.
- If any C++ source under the native/core part of the project was modified, also run the C++ test suite (do not rely on the Python tests alone to cover native changes).

Expand Down Expand Up @@ -53,7 +54,7 @@ Variable-size overlaid types that derive from another overlaid type must use `db

### C++ style

- Use camelCase for local helper variables, lambdas, and method names in C++ code.
- Use snake_case for parameter names and local variable names in C++ code. Parameter names should be concise yet informative. Keep method names consistent with the surrounding code.
- Project types often avoid implicit bool conversion because it can hide subtle ownership, state, and null-check bugs. Use explicit double-negation checks such as `if (!!obj)` or `while (!!item)` when a type supports `operator!()`.

### Python binding wrapper access
Expand Down
2 changes: 1 addition & 1 deletion dbzero/dbzero/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Copyright (c) 2025 DBZero Software sp. z o.o.

from .dbzero import *
from .dbzero import _check_interned, _init_data_masking
from .dbzero import _check_interned, _init_data_filter, _init_data_masking
from .memo import *
from .enum import *
from .fast_query import *
Expand Down
2 changes: 1 addition & 1 deletion dbzero/dbzero/dbzero.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def load_dynamic(name, path):

def __bootstrap__():
global __bootstrap__, __loader__, __file__
paths = [os.path.join(os.path.split(__file__)[0]), "/src/dev/build/release", "/usr/local/lib/python3/dist-packages/dbzero/"]
paths = [os.path.join(os.path.split(__file__)[0]), "/src/dev/build/debug", "/usr/local/lib/python3/dist-packages/dbzero/"]
__file__ = None
for path in paths:
if os.path.isdir(path):
Expand Down
25 changes: 25 additions & 0 deletions dbzero/dbzero/dbzero.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ def get_type_stats(type: type, prefix: Optional[str] = None) -> Dict[str, Any]:
"""
...

def get_prefix_stats(prefix: Optional[str] = None) -> Dict[str, Any]:
"""Retrieve statistics for a prefix.

The result includes ``data_masking["enabled"]`` and ``data_filter["enabled"]``
to report whether the opened prefix has those runtime states attached.
"""
...

# Object retrieval and management

def fetch(identifier: Union[str, type], expected_type: Optional[type] = None, prefix: Optional[str] = None) -> Memo:
Expand Down Expand Up @@ -646,6 +654,14 @@ def _init_data_masking(
"""Initialize workspace-wide or prefix-scoped data masking for the current process."""
...

def _init_data_filter(
context_var: Any,
prefix: Union[str, Any, Sequence[Any], None] = None,
mode: str = "RELEASE",
) -> None:
"""Initialize workspace-wide or prefix-scoped data filtering for the current process."""
...

# Cache management

def clear_cache() -> None:
Expand Down Expand Up @@ -1051,6 +1067,15 @@ def find(*query_criteria: Union[Tag, List[Tag], Tuple[Tag], QueryObject, TagSet]
"""
...

def predicate(*query_criteria: Union[Tag, List[Tag], Tuple[Tag], QueryObject, TagSet], prefix: Optional[str] = None) -> QueryObject:
"""Build a predicate-only query for composing filters.

Predicate queries use the same criteria grammar as ``find`` and can be used
as criteria in other queries or as data-filter predicates. They do not allow
direct iteration, counting, truth testing, indexing, or slicing.
"""
...

def no(predicate: Union[str, QueryObject], /) -> TagSet:
"""Create a negative predicate (NOT condition) for find queries.

Expand Down
9 changes: 8 additions & 1 deletion dbzero/dbzero/initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""dbzero initialization functions"""
from collections.abc import Mapping
from typing import Any
from .dbzero import _init, _init_data_masking, open as dbzero_open
from .dbzero import _init, _init_data_filter, _init_data_masking, open as dbzero_open

def init(dbzero_root: str, **kwargs: Any) -> None:
"""Initialize the dbzero environment in a specified directory and apply global configurations.
Expand All @@ -31,6 +31,7 @@ def init(dbzero_root: str, **kwargs: Any) -> None:
* lang_cache_size (int, default 1024) for language model data cache size
* lock_flags (dict) to configure locking behavior when opening the prefix in read-write mode
* data_masking (dict) to initialize data masking via _init_data_masking
* data_filter (dict) to initialize data filtering via _init_data_filter

Lock flags (dict):
* blocking (bool, default False) wait when trying to acquire the lock
Expand Down Expand Up @@ -63,3 +64,9 @@ def init(dbzero_root: str, **kwargs: Any) -> None:
if not isinstance(data_masking, Mapping):
raise TypeError("data_masking must be a mapping")
_init_data_masking(**data_masking)

if "data_filter" in kwargs:
data_filter = kwargs["data_filter"]
if not isinstance(data_filter, Mapping):
raise TypeError("data_filter must be a mapping")
_init_data_filter(**data_filter)
4 changes: 4 additions & 0 deletions dbzero/dbzero/memo.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ def memo(cls: Optional[type] = None, **kwargs) -> type:
materialized, removing this argument from the Python definition does not clear
the persisted flag; use reset_protect_fields on the dbzero Class object instead.
Derived memo classes inherit field protection and cannot disable it.
access_control : bool, default False
If True, the persistent class is marked as access controlled. This metadata is
recorded on the decorated class and may make loaded base classes report effective
access control at runtime; it does not enable query or fetch enforcement by itself.
intern : bool, default False
If True, the persistent class is marked for interned immutable materialization.
This option requires immutable=True, and interned instances may only reference
Expand Down
18 changes: 15 additions & 3 deletions dbzero/dbzero/reflection_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,18 @@ def __init__(self, param: inspect.Parameter, method: MethodInfo):
class MemoMetaClass:
"""Memo class metadata info."""

def __init__(self, name, module, class_uuid, is_singleton=False, instance_uuid=None):
def __init__(self, name, module, class_uuid, type_flags=None, instance_uuid=None):
self.__name = name
self.__module = module
self.__class_uuid = class_uuid
self.__is_singleton = is_singleton
self.__type_flags = type_flags or {
"singleton": False,
"no_default_tags": False,
"immutable": False,
"intern": False,
"protect_fields": False,
"access_control": False,
}
self.__instance_uuid = instance_uuid
self.__cls = None

Expand Down Expand Up @@ -117,7 +124,12 @@ def get_type(self)-> type:
@property
def is_singleton(self):
"""Is Memo class a singleton."""
return self.__is_singleton
return self.__type_flags["singleton"]

@property
def type_flags(self):
"""Memo class type flags."""
return self.__type_flags

@property
def instance_uuid(self):
Expand Down
Loading
Loading