Module astrapy.data.info.table_descriptor.type_altering
Expand source code
# Copyright DataStax, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any, TypeVar
from astrapy.data.info.table_descriptor.table_columns import (
TableColumnTypeDescriptor,
)
from astrapy.utils.parsing import _warn_residual_keys
AYO = TypeVar("AYO", bound="AlterTypeOperation")
@dataclass
class AlterTypeOperation(ABC):
"""
An abstract class representing a generic "alter type" operation, i.e. a change
to be applied to a user-defined type (UDT) stored on the database. Concrete
implementations are used to represent operations such as adding/renaming fields.
`AlterTypeOperation` objects are used in the Database's `alter_type` method.
Please consult the documentation of the concrete subclasses for more info.
"""
_name: str
@abstractmethod
def as_dict(self) -> dict[str, Any]: ...
@staticmethod
def from_full_dict(operation_dict: dict[str, Any]) -> AlterTypeOperation:
"""
Inspect a provided dictionary and make it into the correct concrete subclass
of AlterTypeOperation depending on its contents.
Note: while the nature of the operation must be the top-level single key of
the (nested) dictionary parameter to this method (such as "add" or
"rename"), the resulting `AlterTypeOperation` object encodes the content
of the corresponding value. Likewise, calling the `as_dict()` method of
the result from this method does not return the whole original input, rather
the "one level in" part (see the example provided here).
Args:
operation_dict: a dictionary such as `{"add": ...}`, whose outermost *value*
corresponds to the desired operation.
Returns:
an `AlterTypeOperation` object chosen after inspection of
the provided input.
Example:
>>> full_dict = {"add": {"fields": {
... "fld1": "text", "fld2": {"type": "int"}
... }}}
>>> alter_op = AlterTypeOperation.from_full_dict(full_dict)
>>> alter_op
AlterTypeAddFields(fields=[fld1,fld2])
>>> alter_op.as_dict()
{'fields': {'fld1': {'type': 'text'}, 'fld2': {'type': 'int'}}}
"""
key_set = set(operation_dict.keys())
if key_set == {"add"}:
return AlterTypeAddFields.coerce(operation_dict["add"])
elif key_set == {"rename"}:
return AlterTypeRenameFields.coerce(operation_dict["rename"])
else:
raise ValueError(
f"Cannot parse a dict with keys {', '.join(sorted(key_set))} "
"into an AlterTypeOperation"
)
@classmethod
@abstractmethod
def coerce(cls: type[AYO], raw_input: AYO | dict[str, Any]) -> AYO: ...
@classmethod
@abstractmethod
def stack(cls: type[AYO], operations: list[AYO]) -> AYO: ...
@classmethod
def stack_by_name(
cls, operations: list[AlterTypeOperation]
) -> dict[str, AlterTypeOperation]:
grouped_ops: dict[str, list[AlterTypeOperation]] = {}
for op in operations:
grouped_ops[op._name] = grouped_ops.get(op._name, []) + [op]
stacked_op_map: dict[str, AlterTypeOperation] = {
op_name: op_list[0].stack(op_list)
for op_name, op_list in grouped_ops.items()
}
return stacked_op_map
@dataclass
class AlterTypeAddFields(AlterTypeOperation):
"""
An object representing the alter-type operation of adding field(s),
for use in the argument to the database's `alter_type()` method.
Attributes:
fields: a mapping between the names of the fields to add and
`TableColumnTypeDescriptor` objects, formatted in the same way as
the `columns` attribute of `CreateTableDefinition`.
"""
fields: dict[str, TableColumnTypeDescriptor]
def __init__(
self,
*,
fields: dict[str, TableColumnTypeDescriptor | dict[str, Any] | str],
) -> None:
self._name = "add"
self.fields = {
fld_n: TableColumnTypeDescriptor.coerce(fld_v)
for fld_n, fld_v in fields.items()
}
def __repr__(self) -> str:
_fld_desc = f"fields=[{','.join(sorted(self.fields.keys()))}]"
return f"{self.__class__.__name__}({_fld_desc})"
def as_dict(self) -> dict[str, Any]:
"""Recast this object into a dictionary."""
return {
"fields": {fld_n: fld_v.as_dict() for fld_n, fld_v in self.fields.items()}
}
@classmethod
def _from_dict(cls, raw_dict: dict[str, Any]) -> AlterTypeAddFields:
"""
Create an instance of AlterTypeAddFields from a dictionary
such as one suitable as (partial) command payload.
"""
_warn_residual_keys(cls, raw_dict, {"fields"})
return AlterTypeAddFields(fields=raw_dict["fields"])
@classmethod
def coerce(
cls, raw_input: AlterTypeAddFields | dict[str, Any]
) -> AlterTypeAddFields:
"""
Normalize the input, whether an object already or a plain dictionary
of the right structure, into an AlterTypeAddFields.
"""
if isinstance(raw_input, AlterTypeAddFields):
return raw_input
else:
return cls._from_dict(raw_input)
@classmethod
def stack(cls, operations: list[AlterTypeAddFields]) -> AlterTypeAddFields:
fields_dict: dict[str, TableColumnTypeDescriptor | dict[str, Any] | str] = {}
for ataf in operations:
fields_dict = {**fields_dict, **ataf.fields}
return AlterTypeAddFields(fields=fields_dict)
@dataclass
class AlterTypeRenameFields(AlterTypeOperation):
"""
An object representing the alter-type operation of renaming field(s),
for use in the argument to the database's `alter_type()` method.
Attributes:
fields: a mapping from current to new names for the fields to rename.
"""
fields: dict[str, str]
def __init__(self, *, fields: dict[str, str]) -> None:
self._name = "rename"
self.fields = fields
def __repr__(self) -> str:
_renames = "; ".join(
[f"'{old_n}' -> '{new_n}'" for old_n, new_n in sorted(self.fields.items())]
)
return f"{self.__class__.__name__}(fields=[{_renames}])"
def as_dict(self) -> dict[str, Any]:
"""Recast this object into a dictionary."""
return {
"fields": self.fields,
}
@classmethod
def _from_dict(cls, raw_dict: dict[str, Any]) -> AlterTypeRenameFields:
"""
Create an instance of AlterTypeRenameFields from a dictionary
such as one suitable as (partial) command payload.
"""
_warn_residual_keys(cls, raw_dict, {"fields"})
return AlterTypeRenameFields(
fields={old_n: new_n for old_n, new_n in raw_dict["fields"].items()},
)
@classmethod
def coerce(
cls, raw_input: AlterTypeRenameFields | dict[str, Any]
) -> AlterTypeRenameFields:
"""
Normalize the input, whether an object already or a plain dictionary
of the right structure, into an AlterTypeRenameFields.
"""
if isinstance(raw_input, AlterTypeRenameFields):
return raw_input
else:
return cls._from_dict(raw_input)
@classmethod
def stack(cls, operations: list[AlterTypeRenameFields]) -> AlterTypeRenameFields:
fields_dict: dict[str, str] = {}
for ataf in operations:
fields_dict = {**fields_dict, **ataf.fields}
return AlterTypeRenameFields(fields=fields_dict)
Classes
class AlterTypeAddFields (*, fields: dict[str, TableColumnTypeDescriptor | dict[str, Any] | str])-
An object representing the alter-type operation of adding field(s), for use in the argument to the database's
alter_type()method.Attributes
fields- a mapping between the names of the fields to add and
TableColumnTypeDescriptorobjects, formatted in the same way as thecolumnsattribute ofCreateTableDefinition.
Expand source code
@dataclass class AlterTypeAddFields(AlterTypeOperation): """ An object representing the alter-type operation of adding field(s), for use in the argument to the database's `alter_type()` method. Attributes: fields: a mapping between the names of the fields to add and `TableColumnTypeDescriptor` objects, formatted in the same way as the `columns` attribute of `CreateTableDefinition`. """ fields: dict[str, TableColumnTypeDescriptor] def __init__( self, *, fields: dict[str, TableColumnTypeDescriptor | dict[str, Any] | str], ) -> None: self._name = "add" self.fields = { fld_n: TableColumnTypeDescriptor.coerce(fld_v) for fld_n, fld_v in fields.items() } def __repr__(self) -> str: _fld_desc = f"fields=[{','.join(sorted(self.fields.keys()))}]" return f"{self.__class__.__name__}({_fld_desc})" def as_dict(self) -> dict[str, Any]: """Recast this object into a dictionary.""" return { "fields": {fld_n: fld_v.as_dict() for fld_n, fld_v in self.fields.items()} } @classmethod def _from_dict(cls, raw_dict: dict[str, Any]) -> AlterTypeAddFields: """ Create an instance of AlterTypeAddFields from a dictionary such as one suitable as (partial) command payload. """ _warn_residual_keys(cls, raw_dict, {"fields"}) return AlterTypeAddFields(fields=raw_dict["fields"]) @classmethod def coerce( cls, raw_input: AlterTypeAddFields | dict[str, Any] ) -> AlterTypeAddFields: """ Normalize the input, whether an object already or a plain dictionary of the right structure, into an AlterTypeAddFields. """ if isinstance(raw_input, AlterTypeAddFields): return raw_input else: return cls._from_dict(raw_input) @classmethod def stack(cls, operations: list[AlterTypeAddFields]) -> AlterTypeAddFields: fields_dict: dict[str, TableColumnTypeDescriptor | dict[str, Any] | str] = {} for ataf in operations: fields_dict = {**fields_dict, **ataf.fields} return AlterTypeAddFields(fields=fields_dict)Ancestors
- AlterTypeOperation
- abc.ABC
Class variables
var fields : dict[str, TableColumnTypeDescriptor]
Static methods
def coerce(raw_input: AlterTypeAddFields | dict[str, Any]) ‑> AlterTypeAddFields-
Normalize the input, whether an object already or a plain dictionary of the right structure, into an AlterTypeAddFields.
Expand source code
@classmethod def coerce( cls, raw_input: AlterTypeAddFields | dict[str, Any] ) -> AlterTypeAddFields: """ Normalize the input, whether an object already or a plain dictionary of the right structure, into an AlterTypeAddFields. """ if isinstance(raw_input, AlterTypeAddFields): return raw_input else: return cls._from_dict(raw_input) def stack(operations: list[AlterTypeAddFields]) ‑> AlterTypeAddFields-
Expand source code
@classmethod def stack(cls, operations: list[AlterTypeAddFields]) -> AlterTypeAddFields: fields_dict: dict[str, TableColumnTypeDescriptor | dict[str, Any] | str] = {} for ataf in operations: fields_dict = {**fields_dict, **ataf.fields} return AlterTypeAddFields(fields=fields_dict)
Methods
def as_dict(self) ‑> dict[str, typing.Any]-
Recast this object into a dictionary.
Expand source code
def as_dict(self) -> dict[str, Any]: """Recast this object into a dictionary.""" return { "fields": {fld_n: fld_v.as_dict() for fld_n, fld_v in self.fields.items()} }
Inherited members
class AlterTypeOperation (_name: str)-
An abstract class representing a generic "alter type" operation, i.e. a change to be applied to a user-defined type (UDT) stored on the database. Concrete implementations are used to represent operations such as adding/renaming fields.
AlterTypeOperationobjects are used in the Database'salter_typemethod.Please consult the documentation of the concrete subclasses for more info.
Expand source code
@dataclass class AlterTypeOperation(ABC): """ An abstract class representing a generic "alter type" operation, i.e. a change to be applied to a user-defined type (UDT) stored on the database. Concrete implementations are used to represent operations such as adding/renaming fields. `AlterTypeOperation` objects are used in the Database's `alter_type` method. Please consult the documentation of the concrete subclasses for more info. """ _name: str @abstractmethod def as_dict(self) -> dict[str, Any]: ... @staticmethod def from_full_dict(operation_dict: dict[str, Any]) -> AlterTypeOperation: """ Inspect a provided dictionary and make it into the correct concrete subclass of AlterTypeOperation depending on its contents. Note: while the nature of the operation must be the top-level single key of the (nested) dictionary parameter to this method (such as "add" or "rename"), the resulting `AlterTypeOperation` object encodes the content of the corresponding value. Likewise, calling the `as_dict()` method of the result from this method does not return the whole original input, rather the "one level in" part (see the example provided here). Args: operation_dict: a dictionary such as `{"add": ...}`, whose outermost *value* corresponds to the desired operation. Returns: an `AlterTypeOperation` object chosen after inspection of the provided input. Example: >>> full_dict = {"add": {"fields": { ... "fld1": "text", "fld2": {"type": "int"} ... }}} >>> alter_op = AlterTypeOperation.from_full_dict(full_dict) >>> alter_op AlterTypeAddFields(fields=[fld1,fld2]) >>> alter_op.as_dict() {'fields': {'fld1': {'type': 'text'}, 'fld2': {'type': 'int'}}} """ key_set = set(operation_dict.keys()) if key_set == {"add"}: return AlterTypeAddFields.coerce(operation_dict["add"]) elif key_set == {"rename"}: return AlterTypeRenameFields.coerce(operation_dict["rename"]) else: raise ValueError( f"Cannot parse a dict with keys {', '.join(sorted(key_set))} " "into an AlterTypeOperation" ) @classmethod @abstractmethod def coerce(cls: type[AYO], raw_input: AYO | dict[str, Any]) -> AYO: ... @classmethod @abstractmethod def stack(cls: type[AYO], operations: list[AYO]) -> AYO: ... @classmethod def stack_by_name( cls, operations: list[AlterTypeOperation] ) -> dict[str, AlterTypeOperation]: grouped_ops: dict[str, list[AlterTypeOperation]] = {} for op in operations: grouped_ops[op._name] = grouped_ops.get(op._name, []) + [op] stacked_op_map: dict[str, AlterTypeOperation] = { op_name: op_list[0].stack(op_list) for op_name, op_list in grouped_ops.items() } return stacked_op_mapAncestors
- abc.ABC
Subclasses
Static methods
def coerce(raw_input: AYO | dict[str, Any]) ‑> ~AYO-
Expand source code
@classmethod @abstractmethod def coerce(cls: type[AYO], raw_input: AYO | dict[str, Any]) -> AYO: ... def from_full_dict(operation_dict: dict[str, Any]) ‑> AlterTypeOperation-
Inspect a provided dictionary and make it into the correct concrete subclass of AlterTypeOperation depending on its contents.
Note: while the nature of the operation must be the top-level single key of the (nested) dictionary parameter to this method (such as "add" or "rename"), the resulting
AlterTypeOperationobject encodes the content of the corresponding value. Likewise, calling theas_dict()method of the result from this method does not return the whole original input, rather the "one level in" part (see the example provided here).Args
operation_dict- a dictionary such as
{"add": ...}, whose outermost value
corresponds to the desired operation.
Returns
an
AlterTypeOperationobject chosen after inspection of the provided input.Example
>>> full_dict = {"add": {"fields": { ... "fld1": "text", "fld2": {"type": "int"} ... }}} >>> alter_op = AlterTypeOperation.from_full_dict(full_dict) >>> alter_op AlterTypeAddFields(fields=[fld1,fld2]) >>> alter_op.as_dict() {'fields': {'fld1': {'type': 'text'}, 'fld2': {'type': 'int'}}}Expand source code
@staticmethod def from_full_dict(operation_dict: dict[str, Any]) -> AlterTypeOperation: """ Inspect a provided dictionary and make it into the correct concrete subclass of AlterTypeOperation depending on its contents. Note: while the nature of the operation must be the top-level single key of the (nested) dictionary parameter to this method (such as "add" or "rename"), the resulting `AlterTypeOperation` object encodes the content of the corresponding value. Likewise, calling the `as_dict()` method of the result from this method does not return the whole original input, rather the "one level in" part (see the example provided here). Args: operation_dict: a dictionary such as `{"add": ...}`, whose outermost *value* corresponds to the desired operation. Returns: an `AlterTypeOperation` object chosen after inspection of the provided input. Example: >>> full_dict = {"add": {"fields": { ... "fld1": "text", "fld2": {"type": "int"} ... }}} >>> alter_op = AlterTypeOperation.from_full_dict(full_dict) >>> alter_op AlterTypeAddFields(fields=[fld1,fld2]) >>> alter_op.as_dict() {'fields': {'fld1': {'type': 'text'}, 'fld2': {'type': 'int'}}} """ key_set = set(operation_dict.keys()) if key_set == {"add"}: return AlterTypeAddFields.coerce(operation_dict["add"]) elif key_set == {"rename"}: return AlterTypeRenameFields.coerce(operation_dict["rename"]) else: raise ValueError( f"Cannot parse a dict with keys {', '.join(sorted(key_set))} " "into an AlterTypeOperation" ) def stack(operations: list[AYO]) ‑> ~AYO-
Expand source code
@classmethod @abstractmethod def stack(cls: type[AYO], operations: list[AYO]) -> AYO: ... def stack_by_name(operations: list[AlterTypeOperation]) ‑> dict[str, AlterTypeOperation]-
Expand source code
@classmethod def stack_by_name( cls, operations: list[AlterTypeOperation] ) -> dict[str, AlterTypeOperation]: grouped_ops: dict[str, list[AlterTypeOperation]] = {} for op in operations: grouped_ops[op._name] = grouped_ops.get(op._name, []) + [op] stacked_op_map: dict[str, AlterTypeOperation] = { op_name: op_list[0].stack(op_list) for op_name, op_list in grouped_ops.items() } return stacked_op_map
Methods
def as_dict(self) ‑> dict[str, typing.Any]-
Expand source code
@abstractmethod def as_dict(self) -> dict[str, Any]: ...
class AlterTypeRenameFields (*, fields: dict[str, str])-
An object representing the alter-type operation of renaming field(s), for use in the argument to the database's
alter_type()method.Attributes
fields- a mapping from current to new names for the fields to rename.
Expand source code
@dataclass class AlterTypeRenameFields(AlterTypeOperation): """ An object representing the alter-type operation of renaming field(s), for use in the argument to the database's `alter_type()` method. Attributes: fields: a mapping from current to new names for the fields to rename. """ fields: dict[str, str] def __init__(self, *, fields: dict[str, str]) -> None: self._name = "rename" self.fields = fields def __repr__(self) -> str: _renames = "; ".join( [f"'{old_n}' -> '{new_n}'" for old_n, new_n in sorted(self.fields.items())] ) return f"{self.__class__.__name__}(fields=[{_renames}])" def as_dict(self) -> dict[str, Any]: """Recast this object into a dictionary.""" return { "fields": self.fields, } @classmethod def _from_dict(cls, raw_dict: dict[str, Any]) -> AlterTypeRenameFields: """ Create an instance of AlterTypeRenameFields from a dictionary such as one suitable as (partial) command payload. """ _warn_residual_keys(cls, raw_dict, {"fields"}) return AlterTypeRenameFields( fields={old_n: new_n for old_n, new_n in raw_dict["fields"].items()}, ) @classmethod def coerce( cls, raw_input: AlterTypeRenameFields | dict[str, Any] ) -> AlterTypeRenameFields: """ Normalize the input, whether an object already or a plain dictionary of the right structure, into an AlterTypeRenameFields. """ if isinstance(raw_input, AlterTypeRenameFields): return raw_input else: return cls._from_dict(raw_input) @classmethod def stack(cls, operations: list[AlterTypeRenameFields]) -> AlterTypeRenameFields: fields_dict: dict[str, str] = {} for ataf in operations: fields_dict = {**fields_dict, **ataf.fields} return AlterTypeRenameFields(fields=fields_dict)Ancestors
- AlterTypeOperation
- abc.ABC
Class variables
var fields : dict[str, str]
Static methods
def coerce(raw_input: AlterTypeRenameFields | dict[str, Any]) ‑> AlterTypeRenameFields-
Normalize the input, whether an object already or a plain dictionary of the right structure, into an AlterTypeRenameFields.
Expand source code
@classmethod def coerce( cls, raw_input: AlterTypeRenameFields | dict[str, Any] ) -> AlterTypeRenameFields: """ Normalize the input, whether an object already or a plain dictionary of the right structure, into an AlterTypeRenameFields. """ if isinstance(raw_input, AlterTypeRenameFields): return raw_input else: return cls._from_dict(raw_input) def stack(operations: list[AlterTypeRenameFields]) ‑> AlterTypeRenameFields-
Expand source code
@classmethod def stack(cls, operations: list[AlterTypeRenameFields]) -> AlterTypeRenameFields: fields_dict: dict[str, str] = {} for ataf in operations: fields_dict = {**fields_dict, **ataf.fields} return AlterTypeRenameFields(fields=fields_dict)
Methods
def as_dict(self) ‑> dict[str, typing.Any]-
Recast this object into a dictionary.
Expand source code
def as_dict(self) -> dict[str, Any]: """Recast this object into a dictionary.""" return { "fields": self.fields, }
Inherited members