Skip to content

Commit

Permalink
CtxMixin classmethods (#1555)
Browse files Browse the repository at this point in the history
* Replace _capsule parameter in __init__ with a CtxMixin.from_capsule() classmethod

* Replace _lt_obj parameter in __init__ with a CtxMixin.from_pybind11() classmethod

* Drop _lt_obj parameter from tiledb.Config as well
  • Loading branch information
gsakkis authored Jan 12, 2023
1 parent 92c0f7b commit 79ec039
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 93 deletions.
27 changes: 7 additions & 20 deletions tiledb/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,19 @@ def __init__(
nullable: bool = False,
filters: Union[FilterList, Sequence[Filter]] = None,
ctx: "Ctx" = None,
_lt_obj: lt.Attribute = None,
_capsule: "PyCapsule" = None,
):
"""Class representing a TileDB array attribute.
:param tiledb.Ctx ctx: A TileDB Context
:param str name: Attribute name, empty if anonymous
:param dtype: Attribute value datatypes
:type dtype: numpy.dtype object or type or string
:param nullable: Attribute is nullable
:type bool:
:param fill: Fill value for unset cells.
:param name: Attribute name, empty if anonymous
:param dtype: Attribute value datatype
:param fill: Fill value for unset cells
:param var: Attribute is variable-length (automatic for byte/string types)
:type dtype: bool
:param nullable: Attribute is nullable
:param filters: List of filters to apply
:type filters: FilterList
:param ctx: A TileDB Context
:raises TypeError: invalid dtype
:raises: :py:exc:`tiledb.TileDBError`
:raises tiledb.TileDBError:
"""
if _capsule is not None:
return super().__init__(ctx, _capsule)

if _lt_obj is not None:
return super().__init__(ctx, _lt_obj=_lt_obj)

_dtype = None
if isinstance(dtype, str) and dtype == "ascii":
tiledb_dtype = lt.DataType.STRING_ASCII
Expand Down Expand Up @@ -171,7 +158,7 @@ def filters(self) -> FilterList:
:raises: :py:exc:`tiledb.TileDBError`
"""
return FilterList(ctx=self._ctx, _lt_obj=self._filters)
return FilterList.from_pybind11(self._ctx, self._filters)

def _get_fill(self, value, dtype) -> Any:
if dtype in (lt.DataType.CHAR, lt.DataType.BLOB):
Expand Down
40 changes: 29 additions & 11 deletions tiledb/ctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,10 @@ class Config(lt.Config):
:param str path: Set parameter values from persisted Config parameter file
"""

def __init__(self, params: dict = None, path: str = None, _lt_obj=None):
if _lt_obj is not None:
return super().__init__(_lt_obj)

def __init__(self, params: dict = None, path: str = None):
super().__init__()

if path is not None:
self.load(path)

if params is not None:
self.update(params)

Expand Down Expand Up @@ -356,7 +351,10 @@ def __repr__(self):

def config(self):
"""Returns the Config instance associated with the Ctx."""
return Config(_lt_obj=super().config())
new = Config.__new__(Config)
# bypass calling Config.__init__, call lt.Config.__init__ instead
lt.Config.__init__(new, super().config())
return new

def set_tag(self, key: str, value: str):
"""Sets a (string, string) "tag" on the Ctx (internal)."""
Expand Down Expand Up @@ -401,18 +399,38 @@ class CtxMixin:
To use this class, a subclass must:
- Inherit from it first (i.e. `class Foo(CtxMixin, Bar)`, not `class Foo(Bar, CtxMixin)`
- Call super().__init__ by passing `ctx` (tiledb.Ctx or None) as first parameter and
- either zero or more pure Python positional parameters
- or a single `_lt_obj` PyBind11 parameter
zero or more pure Python positional parameters
"""

def __init__(self, ctx, *args, _lt_obj=None):
def __init__(self, ctx, *args, _pass_ctx_to_super=True):
if not ctx:
ctx = default_ctx()
super().__init__(_lt_obj or ctx, *args)

if _pass_ctx_to_super:
super().__init__(ctx, *args)
else:
super().__init__(*args)

# we set this here because if the super().__init__() constructor above fails,
# we don't want to set self._ctx
self._ctx = ctx

@classmethod
def from_capsule(cls, ctx, capsule):
"""Create an instance of this class from a PyCapsule instance"""
# bypass calling self.__init__, call CtxMixin.__init__ instead
self = cls.__new__(cls)
CtxMixin.__init__(self, ctx, capsule)
return self

@classmethod
def from_pybind11(cls, ctx, lt_obj):
"""Create an instance of this class from a PyBind11 instance"""
# bypass calling self.__init__, call CtxMixin.__init__ instead
self = cls.__new__(cls)
CtxMixin.__init__(self, ctx, lt_obj, _pass_ctx_to_super=False)
return self


def check_ipykernel_warn_once():
"""
Expand Down
22 changes: 7 additions & 15 deletions tiledb/dimension.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,28 +91,20 @@ def __init__(
dtype: np.dtype = np.uint64,
var: bool = None,
ctx: "Ctx" = None,
_lt_obj: lt.Dimension = None,
):
"""Class representing a dimension of a TileDB Array.
:param str name: the dimension name, empty if anonymous
:param domain:
:type domain: tuple(int, int) or tuple(float, float)
:param name: Dimension name, empty if anonymous
:param domain: TileDB domain
:param tile: Tile extent
:type tile: int or float
:param filters: List of filters to apply
:type filters: FilterList
:dtype: the Dim numpy dtype object, type object, or string \
that can be corerced into a numpy dtype object
:param dtype: Dimension value datatype
:param var: Dimension is variable-length (automatic for byte/string types)
:param ctx: A TileDB Context
:raises ValueError: invalid domain or tile extent
:raises TypeError: invalid domain, tile extent, or dtype type
:raises: :py:exc:`TileDBError`
:param tiledb.Ctx ctx: A TileDB Context
:raises tiledb.TileDBError:
"""
if _lt_obj is not None:
return super().__init__(ctx, _lt_obj=_lt_obj)

if var is not None:
if var and np.dtype(dtype) not in (np.str_, np.bytes_):
raise TypeError("'var=True' specified for non-str/bytes dtype")
Expand Down Expand Up @@ -288,7 +280,7 @@ def filters(self) -> FilterList:
:raises: :py:exc:`tiledb.TileDBError`
"""
return FilterList(ctx=self._ctx, _lt_obj=self._filters)
return FilterList.from_pybind11(self._ctx, self._filters)

@property
def shape(self) -> Tuple["np.generic", "np.generic"]:
Expand Down
24 changes: 5 additions & 19 deletions tiledb/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,14 @@ class Domain(CtxMixin, lt.Domain):
Represents a TileDB domain.
"""

def __init__(
self,
*dims: Dim,
ctx: "Ctx" = None,
_lt_obj: lt.Domain = None,
_capsule: "PyCapsule" = None,
):
def __init__(self, *dims: Dim, ctx: "Ctx" = None):
"""Class representing the domain of a TileDB Array.
:param *dims*: one or more tiledb.Dim objects up to the Domain's ndim
:param ctx: A TileDB Context
:raises TypeError: All dimensions must have the same dtype
:raises: :py:exc:`TileDBError`
:param tiledb.Ctx ctx: A TileDB Context
:raises tiledb.TileDBError:
"""
if _capsule is not None:
return super().__init__(ctx, _capsule)

if _lt_obj is not None:
return super().__init__(ctx, _lt_obj=_lt_obj)

super().__init__(ctx)

# support passing a list of dims without splatting
Expand Down Expand Up @@ -105,7 +92,7 @@ def __len__(self):

def __iter__(self):
"""Returns a generator object that iterates over the domain's dimension objects"""
return (Dim(_lt_obj=self._dim(i)) for i in range(self.ndim))
return (Dim.from_pybind11(self._ctx, self._dim(i)) for i in range(self.ndim))

def __eq__(self, other):
"""Returns true if Domain is equal to self.
Expand Down Expand Up @@ -198,8 +185,7 @@ def dim(self, dim_id):
raise ValueError(
f"Unsupported dim identifier: '{dim_id!r}' (expected int or str)"
)

return Dim(_lt_obj=self._dim(dim_id))
return Dim.from_pybind11(self._ctx, self._dim(dim_id))

def has_dim(self, name):
"""
Expand Down
28 changes: 9 additions & 19 deletions tiledb/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,26 +732,16 @@ class FilterList(CtxMixin, lt.FilterList):
}

def __init__(
self,
filters: Sequence[Filter] = None,
chunksize: int = None,
ctx: "Ctx" = None,
_lt_obj: lt.FilterList = None,
_capsule: "PyCapsule" = None,
self, filters: Sequence[Filter] = None, chunksize: int = None, ctx: "Ctx" = None
):
if _capsule is not None:
super().__init__(ctx, _capsule)
elif _lt_obj is not None:
super().__init__(ctx, _lt_obj=_lt_obj)
else:
super().__init__(ctx)
if filters is not None:
for f in filters:
if not isinstance(f, Filter):
raise ValueError(
"filters argument must be an iterable of TileDB filter objects"
)
self._add_filter(f)
super().__init__(ctx)
if filters is not None:
for f in filters:
if not isinstance(f, Filter):
raise ValueError(
"filters argument must be an iterable of TileDB filter objects"
)
self._add_filter(f)
if chunksize is not None:
self._chunksize = chunksize

Expand Down
15 changes: 6 additions & 9 deletions tiledb/libtiledb.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1309,8 +1309,7 @@ cdef class ArraySchema(object):
check_error(self.ctx,
tiledb_array_schema_get_offsets_filter_list(
ctx_ptr, self.ptr, &filter_list_ptr))
return FilterList(self.ctx,
_capsule=PyCapsule_New(filter_list_ptr, "fl", NULL))
return FilterList.from_capsule(self.ctx, PyCapsule_New(filter_list_ptr, "fl", NULL))

@property
def coords_filters(self):
Expand All @@ -1324,8 +1323,7 @@ cdef class ArraySchema(object):
check_error(self.ctx,
tiledb_array_schema_get_coords_filter_list(
ctx_ptr, self.ptr, &filter_list_ptr))
return FilterList(self.ctx,
_capsule=PyCapsule_New(filter_list_ptr, "fl", NULL))
return FilterList.from_capsule(self.ctx, PyCapsule_New(filter_list_ptr, "fl", NULL))

@coords_filters.setter
def coords_filters(self, value):
Expand All @@ -1347,8 +1345,7 @@ cdef class ArraySchema(object):
check_error(self.ctx,
tiledb_array_schema_get_validity_filter_list(
ctx_ptr, self.ptr, &validity_list_ptr))
return FilterList(self.ctx,
_capsule=PyCapsule_New(validity_list_ptr, "fl", NULL))
return FilterList.from_capsule(self.ctx, PyCapsule_New(validity_list_ptr, "fl", NULL))

@property
def domain(self):
Expand All @@ -1362,7 +1359,7 @@ cdef class ArraySchema(object):
cdef tiledb_ctx_t* ctx_ptr = safe_ctx_ptr(self.ctx)
check_error(self.ctx,
tiledb_array_schema_get_domain(ctx_ptr, self.ptr, &dom))
return Domain(self.ctx, _capsule=PyCapsule_New(dom, "dom", NULL))
return Domain.from_capsule(self.ctx, PyCapsule_New(dom, "dom", NULL))

@property
def nattr(self):
Expand Down Expand Up @@ -1437,15 +1434,15 @@ cdef class ArraySchema(object):
check_error(self.ctx,
tiledb_array_schema_get_attribute_from_name(
ctx_ptr, self.ptr, bname, &attr_ptr))
return Attr(self.ctx, _capsule=PyCapsule_New(attr_ptr, "attr", NULL))
return Attr.from_capsule(self.ctx, PyCapsule_New(attr_ptr, "attr", NULL))

cdef _attr_idx(self, int idx):
cdef tiledb_attribute_t* attr_ptr = NULL
cdef tiledb_ctx_t* ctx_ptr = safe_ctx_ptr(self.ctx)
check_error(self.ctx,
tiledb_array_schema_get_attribute_from_index(
ctx_ptr, self.ptr, idx, &attr_ptr))
return Attr(self.ctx, _capsule=PyCapsule_New(attr_ptr, "attr", NULL))
return Attr.from_capsule(self.ctx, PyCapsule_New(attr_ptr, "attr", NULL))

def attr(self, object key not None):
"""Returns an Attr instance given an int index or string label
Expand Down

0 comments on commit 79ec039

Please sign in to comment.