from abc import abstractmethod
from numbers import Integral, Real
from typing import Any, ClassVar, Dict, Optional, TYPE_CHECKING, Tuple, TypeVar, Union
from warnings import warn
from attr import attrib, attrs, evolve
from ..errors import CellDSLError
from ..formats import FormatDict, FormatHandler, FormatsNamespace
from ..utils import WorksheetTriplet
if TYPE_CHECKING:
pass
T = TypeVar('T')
Coords = Tuple[int, int]
CellPointer = Union[str, int, Coords]
@attrs(auto_attribs=True)
class Trait(object):
pass
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class FractionalSize(Trait):
"""Commands with this trait require some kind of real-valued `size`"""
size: float = 0.0
[docs] def with_size(self: T, size: Real) -> T:
"""Specify the real-valued `size` for this object."""
return evolve(self, size=float(size))
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class CardinalSize(Trait):
"""Commands with this trait require some kind of integer `size`"""
size: int = 0
[docs] def with_size(self: T, size: Integral) -> T:
"""Specify the integral `size` for this object."""
return evolve(self, size=int(size))
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class AbsolutePosition(Trait):
"""Commands with this aspect target a specific cell at `row` and `col`"""
row: int = -1
col: int = -1
[docs] def r(self: T, row: Integral) -> T:
"""Target the cell at `row` for this object."""
return evolve(self, row=int(row))
[docs] def c(self: T, col: Integral) -> T:
"""Target the cell at `col` for this object."""
return evolve(self, col=int(col))
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class RelativePosition(Trait):
"""Commands with this trait target a cell relative to the current cell."""
row: int = 0
col: int = 0
[docs] def r(self: T, delta_row: Integral) -> T:
"""Specify a target `delta_row` away from current position for this object."""
return evolve(self, row=int(delta_row))
[docs] def c(self: T, delta_col: Integral) -> T:
"""Specify a target `delta_col` away from current position for this object."""
return evolve(self, col=int(delta_col))
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class Range(Trait):
"""
Commands with this trait target a range of cells bounded by a box with `top_left_point` and `bottom_right_point`.
Notes:
`top_left_point` and `bottom_right_point` have different behavior depending on the value type
* If it's a string, this will be the save point name at which the save occurs.
* If it's an Integral
* If positive: this will be last n-th visited cell.
* If negative: this will be last n-th position in save stack which will be retrieved without popping it
from the stack.
* If zero, current cell will be the target.
* If it's Coords, it will be the absolute coords of the cell.
"""
top_left_point: CellPointer = 0
bottom_right_point: CellPointer = 0
[docs] def with_top_left(self, point: CellPointer):
"""Specify top left corner `point` for this object."""
return evolve(self, top_left_point=point)
[docs] def with_bottom_right(self, point: CellPointer):
"""Specify bottom right corner `point` for this object."""
return evolve(self, bottom_right_point=point)
def top_left(self, point: CellPointer):
warn("This method is deprecated in favor or `with_top_left`", DeprecationWarning)
return self.with_top_left(point)
def bottom_right(self, point: CellPointer):
warn("This method is deprecated in favor or `with_bottom_right`", DeprecationWarning)
return self.with_bottom_right(point)
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class Data(Trait):
"""Commands with this trait provide some `data` to the function for writing."""
data: Any = ""
[docs] def with_data(self: T, data: Any) -> T:
"""Specify the `data` for this object."""
return evolve(self, data=data)
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class DataType(Trait):
"""Commands with this trait parametrize `data_type`, generally assuming :class:`Data` trait is also present."""
data_type: Optional[str] = None
ACCEPTED_DATA_TYPES = {None, 'string', 'number', 'blank', 'formula', 'datetime', 'boolean', 'url'}
[docs] def with_data_type(self, data_type: Optional[str]):
"""Force the write to use a specific method or default to generic `write` if `data_type` is None.
Accepted values are: string, number, blank, formula, datetime, boolean, url and None
See Also:
:func:`write`
"""
if data_type not in self.ACCEPTED_DATA_TYPES:
raise CellDSLError(
f'Data type {data_type} is not valid: valid values are {self.ACCEPTED_DATA_TYPES}'
)
return evolve(self, data_type=data_type)
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class NamedPoint(Trait):
"""Commands with this trait give a temporary `point_name` to the cell to be referenced later."""
point_name: str = "__DEFAULT"
[docs] def at(self: T, point_name) -> T:
"""Specify the `point_name` for this object."""
return evolve(self, point_name=point_name)
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class ForwardRef(Trait):
"""Commands with this trait manage a list of `resolve_refs`."""
resolved_refs: Dict[str, str] = attrib(factory=dict, repr=False, hash=False)
def inject_refs(self: T, ref_array) -> T:
"""Not for public use; inject the `ref_array` into this object."""
return evolve(self, resolved_refs=ref_array)
[docs]@attrs(auto_attribs=True, frozen=True, order=False)
class Options(Trait):
"""Commands with this trait have a dictionary of `options`"""
options: Dict[str, Any] = attrib(factory=dict, repr=False, hash=False)
[docs] def with_options(self, options: Dict[str, Any]):
"""Add new `options` to our configuration."""
return evolve(self, options={**self.options, **options})
[docs]class ExecutableCommand(Trait):
"""Commands with this trait are active commands representing actions that will be done in the worksheet."""
@abstractmethod
def execute(self, target: WorksheetTriplet, coords: Coords):
"""Not for public use; execute the command at `coords` in `target`."""
raise NotImplemented
__all__ = [
'FractionalSize', 'CardinalSize',
'RelativePosition', 'AbsolutePosition',
'Range',
'Data', 'DataType',
'Format',
'NamedPoint', 'ForwardRef',
'Options',
'ExecutableCommand',
'Coords', 'CellPointer'
]