1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
""" util.py
"""
import six
__all__ = ('memoryview', )
memoryview = memoryview
if six.PY2:
# because Python2's memoryview can't be released directly, nor can it be used as a context manager
# this wrapper object should hopefully make the behavior more uniform to python 3's
import __builtin__
import functools
# this decorator will raise a ValueError if the wrapped memoryview object has been "released"
def notreleased(meth):
@functools.wraps(meth)
def _inner(self, *args, **kwargs):
if self._mem is None:
raise ValueError("operation forbidden on released memoryview object")
return meth(self, *args, **kwargs)
return _inner
class memoryview(object): # flake8: noqa
@property
@notreleased
def obj(self):
"""The underlying object of the memoryview."""
return self._obj
@property
@notreleased
def nbytes(self):
# nbytes == product(shape) * itemsize == len(m.tobytes())
nb = 1
for dim in self.shape:
nb *= dim
return nb * self.itemsize
# TODO: c_contiguous -> (self.ndim == 0 or ???)
# TODO: f_contiguous -> (self.ndim == 0 or ???)
# TODO: contiguous -> return self.c_contiguous or self.f_contiguous
def __new__(cls, obj, parent=None):
memview = object.__new__(cls)
memview._obj = obj if parent is None else parent.obj
return memview
def __init__(self, obj):
if not hasattr(self, '_mem'):
if not isinstance(obj, __builtin__.memoryview):
obj = __builtin__.memoryview(obj)
self._mem = obj
def __dir__(self):
# so dir(...) looks like a memoryview object, and also
# contains our additional methods and properties, but not our instance members
return sorted(set(self.__class__.__dict__) | set(dir(self._mem)))
@notreleased
def __getitem__(self, item):
# if this is a slice, it'll return another real memoryview object
# we'll need to wrap that subview in another memoryview wrapper
if isinstance(item, slice):
return memoryview(self._mem.__getitem__(item))
return self._mem.__getitem__(item)
@notreleased
def __setitem__(self, key, value):
self._mem.__setitem__(key, value)
@notreleased
def __delitem__(self, key):
raise TypeError("cannot delete memory")
def __getattribute__(self, item):
try:
return object.__getattribute__(self, item)
except AttributeError:
if object.__getattribute__(self, '_mem') is None:
raise ValueError("operation forbidden on released memoryview object")
return object.__getattribute__(self, '_mem').__getattribute__(item)
def __setattr__(self, key, value):
if key not in self.__dict__ and hasattr(__builtin__.memoryview, key):
# there are no writable attributes on memoryview objects
# changing indexed values is handled by __setitem__
raise AttributeError("attribute '{}' of 'memoryview' objects is not writable".format(key))
else:
object.__setattr__(self, key, value)
@notreleased
def __len__(self):
return len(self._mem)
def __eq__(self, other):
if isinstance(other, memoryview):
return self._mem == other._mem
return self._mem == other
@notreleased
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.release()
def __repr__(self):
return '<{}memory at 0x{:02X}>'.format('' if self._mem else 'released ', id(self))
def release(self):
"""Release the underlying buffer exposed by the memoryview object"""
# this should effectively do the same job as memoryview.release() in Python 3
self._mem = None
self._obj = None
@notreleased
def hex(self):
"""Return the data in the buffer as a string of hexadecimal numbers."""
return ''.join(('{:02X}'.format(ord(c)) for c in self._mem))
# TODO: cast
|