annotate workspace.py @ 1:173e86601dbc

Add views.
author David Barts <n5jrn@me.com>
date Thu, 26 Dec 2019 12:00:03 -0800
parents 984876b6a095
children 8884b0bf779d
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
0
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
1 #!/usr/bin/env python3
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
2 # -*- coding: utf-8 -*-
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
3
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
4 # A class that implements a workspace for curly-quoting a text. This is enough
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
5 # like a string that it can be accessed via subscripts and ranges, and enough
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
6 # like a TextIOBase object that it can be written to much like a stream.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
7 # (However, a Workspace is neither a string nor a TextIOBase object.)
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
8 #
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
9 # The advantage of using UTF-16 (as we do here) is that all quotation marks
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
10 # of interest are represented in a single 16-bit value, so changing straight
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
11 # quotes to curly ones can be accomplished most easily.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
12 #
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
13 # It was a deliberate design decision to return empty strings when reading
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
14 # out-of-range indices but to throw exceptions when attempting to write
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
15 # them, because both decisions made coding easier in other modules.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
16
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
17 # I m p o r t s
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
18
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
19 import os, sys
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
20 import io
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
21 import codecs
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
22
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
23 # V a r i a b l e s
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
24
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
25 # C l a s s e s
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
26
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
27 class Workspace(object):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
28 # The most efficient 16-bit one on this platform
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
29 encoding = "UTF-16" + sys.byteorder[0].upper() + "E"
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
30 codec = codecs.lookup(encoding)
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
31 # Errors should never happen; UTF-16 can represent all Unicode characters
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
32 errors = 'strict'
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
33
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
34 def __init__(self, initial_data=None):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
35 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
36 Constructor.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
37 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
38 self._length = 0
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
39 if initial_data is not None:
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
40 data = initial_data.encode(self.encoding, self.errors)
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
41 self._fp = io.BytesIO(data)
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
42 else:
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
43 self._fp = io.BytesIO()
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
44
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
45 def close(self):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
46 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
47 Causes our buffer to be discarded and this workspace to become
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
48 unusable.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
49 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
50 self._fp.close()
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
51
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
52 def flush(self):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
53 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
54 Does nothing, but allowed.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
55 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
56 pass
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
57
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
58 def seek(self, offset, whence=io.SEEK_SET):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
59 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
60 Seeks to an absolute position.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
61 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
62 return self._fp.seek(offset, whence)
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
63
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
64 def tell(self):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
65 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
66 Returns current position.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
67 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
68 return self._fp.tell()
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
69
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
70 def read(self, nchars=None):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
71 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
72 Read characters.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
73 XXX - might return replacement chars from surrogate fragments.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
74 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
75 if nchars is not None and nchars >= 0:
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
76 nchars *= 2
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
77 return self._fp.read(nchars).decode(self.encoding, "replace")
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
78
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
79 def write(self, string):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
80 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
81 Write characters.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
82 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
83 self._fp.write(string.encode(self.encoding, self.errors))
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
84
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
85 def __len__(self):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
86 """
1
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
87 Length in characters.
0
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
88 """
1
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
89 return len(self._fp.getbuffer()) // 2
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
90
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
91 def _mapped(self, index):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
92 if index < 0 or index >= len(self):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
93 raise IndexError("index {0} out of range".format(index))
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
94 i2 = index * 2
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
95 return slice(i2, i2 + 2)
0
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
96
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
97 def __getitem__(self, key):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
98 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
99 Direct access to a single character or range of characters. We do
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
100 not support negative indices. Return value is based on what's most
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
101 useful for curling quotes.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
102 XXX - might return replacement chars from surrogate fragments.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
103 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
104 if isinstance(key, int):
1
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
105 try:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
106 key = self._mapped(key)
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
107 except IndexError:
0
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
108 return ""
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
109 elif isinstance(key, slice):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
110 if key.step is not None:
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
111 raise ValueError("__getitem__ does not support steps in slices")
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
112 length = len(self)
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
113 start = 0 if key.start is None else key.start
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
114 stop = length if key.stop is None else key.stop
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
115 start = max(0, min(length - 1, start))
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
116 stop = max(0, min(length, stop))
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
117 if stop <= start:
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
118 return ""
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
119 key = slice(start * 2, stop * 2)
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
120 else:
1
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
121 raise TypeError("__getitem__ only supports integers and slices")
0
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
122 return self.codec.decode(self._fp.getbuffer()[key], "replace")[0]
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
123
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
124 def __setitem__(self, key, value):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
125 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
126 Direct access to a single character. We do not support negative
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
127 indices or replacing more than a single character at a time.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
128 XXX - only works on characters in the BMP.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
129 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
130 if not isinstance(key, int):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
131 raise TypeError("__setitem__ only supports integers")
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
132 if not value:
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
133 return
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
134 encoded = value[0].encode(self.encoding, self.errors)
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
135 if len(encoded) != 2:
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
136 raise ValueError("{0!r} not in BMP".format(value[0]))
1
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
137 self._fp.getbuffer()[self._mapped(key)] = encoded
0
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
138
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
139 def __del__(self):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
140 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
141 Equivalent to .close().
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
142 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
143 self.close()
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
144
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
145 def getvalue(self):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
146 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
147 Gets the string represented by this workspace.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
148 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
149 return self.codec.decode(self._fp.getbuffer(), self.errors)[0]
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
150
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
151 def __enter__(self):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
152 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
153 Context manager.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
154 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
155 return self
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
156
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
157 def __exit__(self, exc_type, exc_val, exc_tb):
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
158 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
159 Context manager: close on exit.
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
160 """
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
161 self.close()
984876b6a095 Initial commit of first two classes.
David Barts <n5jrn@me.com>
parents:
diff changeset
162 return False
1
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
163
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
164 class Bounds(object):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
165 def __init__(self, start, stop):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
166 if start > stop or start < 0 or stop < 0:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
167 raise ValueError("invalid bounds")
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
168 self.start = int(start)
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
169 self.stop = int(stop)
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
170
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
171 @classmethod
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
172 def from_object(cls, obj):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
173 if isinstance(obj, slice):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
174 return self(slice.start, slice.stop)
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
175 return self(obj[0], obj[1])
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
176
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
177 def __lt__(self, other):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
178 return self.start < other.start
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
179
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
180 def __le__(self, other):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
181 return self.start <= other.start
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
182
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
183 def __eq__(self, other):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
184 return self.start == other.start
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
185
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
186 def __ne__(self, other):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
187 return self.start != other.start
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
188
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
189 def __gt__(self, other):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
190 return self.start > other.start
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
191
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
192 def __ge__(self, other):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
193 return self.start >= other.start
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
194
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
195 def __contains__(self, scalar):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
196 return self.start <= scalar < self.stop
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
197
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
198 def __repr__(self):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
199 return "{0}({1!r}, {2!r})".format(self.__class__.__name__, self.start, self.stop)
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
200
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
201 class Mapping(object):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
202 def __init__(self, bounds, offset):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
203 if not isinstance(bounds, Bounds):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
204 raise TypeError("bounds must be a Bounds object")
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
205 if not isinstance(offset, int):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
206 raise TypeError("offset must be an int")
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
207 self.bounds = bounds
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
208 self.offset = offset
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
209
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
210 def __repr__(self):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
211 return "{0}({1!r}, {2!r})".format(self.__class__.__name__, self.bounds, self.offset)
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
212
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
213 class SegmentedView(object):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
214 """
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
215 Implements a view on a subscriptable object. The view is composed of
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
216 zero or more segments of the source object. Has the same idiosyncratic
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
217 behavior for out-of-bounds indices that Workspace has (and for the
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
218 same reason).
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
219 """
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
220 def __init__(self, indexable, bounds):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
221 self.indexable = indexable
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
222 self._mmap = [ Mapping(Bounds(0, 0), 0) ]
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
223 pos = 0
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
224 for r in sorted(bounds):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
225 if pos is not None and r.start <= pos and r.stop > pos:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
226 # merge ranges
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
227 self._mmap[-1].bounds.stop = r.stop
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
228 pos = r.stop
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
229 continue
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
230 opos = pos
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
231 pos += r.stop - r.start
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
232 self._mmap.append(Mapping(Bounds(opos, pos), r.start))
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
233 self._length = pos
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
234
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
235 def _mapped(self, index):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
236 mi = self._binsch(index)
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
237 m = None if mi is None else self._mmap[mi]
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
238 if m is None:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
239 raise IndexError("index {0} out of range".format(index))
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
240 return index - m.bounds.start + m.offset
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
241
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
242 def _binsch(self, index):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
243 a = 0
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
244 z = len(self._mmap) - 1
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
245 while a <= z:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
246 m = (a + z) // 2
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
247 if index in self._mmap[m].bounds:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
248 return m
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
249 if index < self._mmap[m].bounds.start:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
250 z = m - 1
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
251 else:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
252 assert index >= self._mmap[m].bounds.stop
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
253 a = m + 1
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
254 return None
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
255
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
256 def __setitem__(self, key, value):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
257 if not isinstance(key, int):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
258 raise TypeError("__setitem__ only supports integers")
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
259 self.indexable[self._mapped(key)] = value
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
260
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
261 # XXX - this is sorta brute-forced and could be more efficient
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
262 def __getitem__(self, key):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
263 if isinstance(key, int):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
264 return self._get1(key)
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
265 if not isinstance(key, slice):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
266 raise TypeError("expecting int or slice")
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
267 with io.StringIO() as buf:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
268 for i in range(key.start, key.stop, key.step or 1):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
269 buf.write(self._get1(i))
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
270 return buf.getvalue()
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
271
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
272 def __len__(self):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
273 return self._length
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
274
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
275 def _get1(self, index):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
276 try:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
277 return self.indexable[self._mapped(index)]
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
278 except IndexError:
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
279 return ""
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
280
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
281 def getvalue(self):
173e86601dbc Add views.
David Barts <n5jrn@me.com>
parents: 0
diff changeset
282 return self[0:len(self)]