Skip to content

Commit ee07d6e

Browse files
committed
fix larray-project#832 : implemented FrozenSession class
1 parent f4e63f1 commit ee07d6e

File tree

4 files changed

+481
-43
lines changed

4 files changed

+481
-43
lines changed

doc/source/api.rst

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -790,7 +790,6 @@ Modifying
790790

791791
Session.add
792792
Session.update
793-
Session.get
794793
Session.apply
795794
Session.transpose
796795

@@ -816,6 +815,74 @@ Load/Save
816815
Session.to_hdf
817816
Session.to_pickle
818817

818+
.. _api-frozensession:
819+
820+
FrozenSession
821+
=============
822+
823+
.. autosummary::
824+
:toctree: _generated/
825+
826+
FrozenSession
827+
828+
Exploring
829+
---------
830+
831+
.. autosummary::
832+
:toctree: _generated/
833+
834+
FrozenSession.names
835+
FrozenSession.keys
836+
FrozenSession.values
837+
FrozenSession.items
838+
FrozenSession.summary
839+
840+
Copying
841+
-------
842+
843+
.. autosummary::
844+
:toctree: _generated/
845+
846+
FrozenSession.copy
847+
848+
Testing
849+
-------
850+
851+
.. autosummary::
852+
:toctree: _generated/
853+
854+
FrozenSession.element_equals
855+
FrozenSession.equals
856+
857+
Selecting
858+
---------
859+
860+
.. autosummary::
861+
:toctree: _generated/
862+
863+
FrozenSession.get
864+
865+
Modifying
866+
---------
867+
868+
.. autosummary::
869+
:toctree: _generated/
870+
871+
FrozenSession.apply
872+
873+
Load/Save
874+
---------
875+
876+
.. autosummary::
877+
:toctree: _generated/
878+
879+
FrozenSession.load
880+
FrozenSession.save
881+
FrozenSession.to_csv
882+
FrozenSession.to_excel
883+
FrozenSession.to_hdf
884+
FrozenSession.to_pickle
885+
819886
.. _api-editor:
820887

821888
Editor

larray/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
full_like, sequence, labels_array, ndtest, asarray, identity, diag,
1010
eye, all, any, sum, prod, cumsum, cumprod, min, max, mean, ptp, var,
1111
std, median, percentile, stack, zip_array_values, zip_array_items)
12-
from larray.core.session import Session, local_arrays, global_arrays, arrays
12+
from larray.core.session import Session, FrozenSession, local_arrays, global_arrays, arrays
1313
from larray.core.constants import nan, inf, pi, e, euler_gamma
1414
from larray.core.metadata import Metadata
1515
from larray.core.ufuncs import wrap_elementwise_array_func, maximum, minimum, where
@@ -58,7 +58,7 @@
5858
'all', 'any', 'sum', 'prod', 'cumsum', 'cumprod', 'min', 'max', 'mean', 'ptp', 'var', 'std',
5959
'median', 'percentile', 'stack', 'zip_array_values', 'zip_array_items',
6060
# session
61-
'Session', 'local_arrays', 'global_arrays', 'arrays',
61+
'Session', 'FrozenSession', 'local_arrays', 'global_arrays', 'arrays',
6262
# constants
6363
'nan', 'inf', 'pi', 'e', 'euler_gamma',
6464
# metadata

larray/core/session.py

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import re
77
import fnmatch
88
import warnings
9+
from copy import copy
910
from collections import OrderedDict, Iterable
1011

1112
import numpy as np
@@ -91,7 +92,7 @@ def __init__(self, *args, **kwargs):
9192

9293
if len(args) == 1:
9394
a0 = args[0]
94-
if isinstance(a0, str):
95+
if isinstance(a0, basestring):
9596
# assume a0 is a filename
9697
self.load(a0)
9798
else:
@@ -1391,6 +1392,104 @@ def display(k, v, is_metadata=False):
13911392
return res
13921393

13931394

1395+
# XXX: I wonder if we shouldn't create an AbstractSession instead of defining the _disabled()
1396+
# private method below.
1397+
# Auto-completion on any instance of a class that inherits from FrozenSession
1398+
# should not propose the add(), update(), filter(), transpose() and compact() methods
1399+
class FrozenSession(Session):
1400+
1401+
def __init__(self, *args, **kwargs):
1402+
meta = kwargs.pop('meta', None)
1403+
1404+
if len(args) == 1 and isinstance(args[0], basestring):
1405+
filepath, args = args[0], args[1:]
1406+
else:
1407+
filepath = None
1408+
1409+
if args or kwargs:
1410+
raise ValueError("All items of '{cls}' must be defined in the class declaration."
1411+
.format(cls=self.__class__.__name__))
1412+
if meta:
1413+
kwargs['meta'] = meta
1414+
1415+
if filepath:
1416+
args = [filepath]
1417+
else:
1418+
# feed the kwargs dict with all items declared as class attributes
1419+
for key, value in vars(self.__class__).items():
1420+
if not key.startswith('_'):
1421+
kwargs[key] = value
1422+
1423+
Session.__init__(self, *args, **kwargs)
1424+
object.__setattr__(self, 'add', self._disabled)
1425+
1426+
def __setitem__(self, key, value):
1427+
self._check_key_value(key, value)
1428+
1429+
# we need to keep the attribute in sync (initially to mask the class attribute)
1430+
object.__setattr__(self, key, value)
1431+
self._objects[key] = value
1432+
1433+
def __setattr__(self, key, value):
1434+
if key != 'meta':
1435+
self._check_key_value(key, value)
1436+
1437+
# update the real attribute
1438+
object.__setattr__(self, key, value)
1439+
# update self._objects
1440+
Session.__setattr__(self, key, value)
1441+
1442+
def _check_key_value(self, key, value):
1443+
cls = self.__class__
1444+
attr_def = getattr(cls, key, None)
1445+
if attr_def is None:
1446+
raise ValueError("The '{item}' item has not been found in the '{cls}' class declaration. "
1447+
"Adding a new item after creating an instance of the '{cls}' class is not permitted."
1448+
.format(item=key, cls=cls.__name__))
1449+
if (isinstance(value, (int, float, basestring, np.generic)) and value != attr_def) \
1450+
or (isinstance(value, (Axis, Group)) and not value.equals(attr_def)):
1451+
raise TypeError("The '{key}' item is of kind '{cls_name}' which cannot by modified."
1452+
.format(key=key, cls_name=attr_def.__class__.__name__))
1453+
if type(value) != type(attr_def):
1454+
raise TypeError("Expected object of type '{attr_cls}'. Got object of type '{value_cls}'."
1455+
.format(attr_cls=attr_def.__class__.__name__, value_cls=value.__class__.__name__))
1456+
if isinstance(attr_def, Array):
1457+
try:
1458+
attr_def.axes.check_compatible(value.axes)
1459+
except ValueError as e:
1460+
msg = str(e).replace("incompatible axes:", "Incompatible axes for array '{key}':".format(key=key))
1461+
raise ValueError(msg)
1462+
elif isinstance(value, np.ndarray) and value.shape != attr_def.shape:
1463+
raise ValueError("Incompatible shape for Numpy array '{key}'. "
1464+
"Expected shape {attr_shape} but got {value_shape}."
1465+
.format(key=key, attr_shape=attr_def.shape, value_shape=value.shape))
1466+
1467+
def copy(self):
1468+
instance = self.__class__()
1469+
for key, value in self.items():
1470+
instance[key] = copy(value)
1471+
return instance
1472+
1473+
def apply(self, func, *args, **kwargs):
1474+
kind = kwargs.pop('kind', Array)
1475+
instance = self.__class__()
1476+
for key, value in self.items():
1477+
instance[key] = func(value, *args, **kwargs) if isinstance(value, kind) else value
1478+
return instance
1479+
1480+
def _disabled(self, *args, **kwargs):
1481+
"""This method will not work because adding or removing item and modifying axes of declared arrays
1482+
is not permitted."""
1483+
raise ValueError(
1484+
"Adding or removing item and modifying axes of declared arrays is not permitted.".format(
1485+
cls=self.__class__.__name__
1486+
)
1487+
)
1488+
1489+
__delitem__ = __delattr__ = _disabled
1490+
update = filter = transpose = compact = _disabled
1491+
1492+
13941493
def _exclude_private_vars(vars_dict):
13951494
return {k: v for k, v in vars_dict.items() if not k.startswith('_')}
13961495

0 commit comments

Comments
 (0)