monty 0.0.20

A sandboxed, snapshotable Python interpreter written in Rust.
Documentation
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# Basic user-defined classes: construction, instance attributes, methods,
# class variables, type()/isinstance(), identity equality and bound methods.


class Point:
    # class variable shared across instances
    origin_count = 0

    def __init__(self, x: int, y: int) -> None:
        self.x = x
        self.y = y

    def total(self) -> int:
        return self.x + self.y

    def scaled(self, factor: int = 2) -> int:
        return self.total() * factor

    def move(self, dx: int, dy: int) -> None:
        self.x += dx
        self.y += dy


# === Construction and __init__ ===
p = Point(3, 4)
assert p.x == 3
assert p.y == 4

# === Instance methods ===
assert p.total() == 7
assert p.scaled() == 14
assert p.scaled(3) == 21
assert p.scaled(factor=10) == 70

# === Mutating attributes via a method ===
p.move(1, 1)
assert p.x == 4
assert p.y == 5
assert p.total() == 9

# === Mutating attributes directly ===
p.x = 100
assert p.x == 100
assert p.total() == 105

# === Setting a new attribute not declared in __init__ ===
p.z = 7
assert p.z == 7

# === Class variables ===
assert Point.origin_count == 0
assert p.origin_count == 0
q = Point(1, 1)
assert q.origin_count == 0

# === Independent instances ===
assert p.x == 100 and q.x == 1, 'instances have independent attributes'

# === type() returns the class object ===
assert type(p) is Point
assert type(p) is type(q)
assert type(p).__name__ == 'Point'

# === isinstance ===
assert isinstance(p, Point)
assert isinstance(p, (int, Point))
assert not isinstance(5, Point), 'isinstance false for a non-instance'


class Other:
    def __init__(self) -> None:
        self.v = 1


o = Other()
assert not isinstance(o, Point), 'isinstance false for a different class'
assert type(o) is not Point

# === Identity equality (no user __eq__) ===
assert p == p
assert p != q
assert (p == q) is False

# === Custom equality ===


class EqPoint:
    def __init__(self, x: int, y: int) -> None:
        self.x = x
        self.y = y

    def __eq__(self, other):
        if not isinstance(other, EqPoint):
            return NotImplemented
        return self.x == other.x and self.y == other.y


ep1 = EqPoint(1, 2)
ep2 = EqPoint(1, 2)
ep3 = EqPoint(2, 1)
assert ep1 == ep2, 'custom __eq__ compares instance attributes'
assert ep1 != ep3, 'custom __eq__ false result is respected'
assert (ep1 == 1) is False, 'NotImplemented from instance equality falls back to unequal'
assert (1 == ep1) is False, 'NotImplemented works when reflected equality reaches the instance'

try:
    {ep1: 'value'}
    assert False, 'an instance with custom equality should be rejected as a dict key'
except TypeError as exc:
    assert str(exc) == "cannot use 'EqPoint' as a dict key (unhashable type: 'EqPoint')", 'dict key error'

try:
    {ep1}
    assert False, 'an instance with custom equality should be rejected as a set element'
except TypeError as exc:
    assert str(exc) == "cannot use 'EqPoint' as a set element (unhashable type: 'EqPoint')", 'set element error'

try:
    hash(ep1)
    assert False, 'an instance with custom equality should be unhashable'
except TypeError as exc:
    assert str(exc) == "unhashable type: 'EqPoint'", 'custom equality disables identity hashing'


class NeverEqual:
    def __init__(self):
        self.calls = 0

    def __eq__(self, other):
        self.calls += 1
        return False


never_equal = NeverEqual()
assert (never_equal == never_equal) is False, 'custom equality runs before the identity fallback'
assert never_equal.calls == 1
assert (never_equal != never_equal) is True, 'inequality negates custom equality for the same object'
assert never_equal.calls == 2
assert never_equal in [never_equal], 'list membership accepts an identical object before equality'
assert [never_equal].count(never_equal) == 1, 'list.count accepts an identical object before equality'
assert [never_equal].index(never_equal) == 0, 'list.index accepts an identical object before equality'
assert never_equal in (never_equal,), 'tuple membership accepts an identical object before equality'
assert [never_equal] == [never_equal], 'list equality accepts identical elements before equality'
assert (never_equal,) == (never_equal,), 'tuple equality accepts identical elements before equality'
assert never_equal.calls == 2


class LeftEq:
    def __eq__(self, other):
        return NotImplemented


class RightEq:
    def __eq__(self, other):
        return 'right handled'


left_eq = LeftEq()
assert left_eq == left_eq, 'NotImplemented falls back to identity for self-comparison'
assert (left_eq == LeftEq()) is False, 'NotImplemented falls back to unequal for distinct objects'
assert (left_eq == RightEq()) == 'right handled', 'reflected __eq__ preserves its arbitrary result'
assert (RightEq() == left_eq) == 'right handled', 'custom __eq__ preserves its arbitrary result'
assert left_eq in [RightEq()], 'container equality truth-tests a reflected arbitrary result'


class HeapResultEq:
    def __eq__(self, other):
        return []


heap_result_eq = HeapResultEq()
heap_eq_result = heap_result_eq == 1
assert heap_eq_result == []
assert heap_result_eq not in [1], 'container membership truth-tests a heap-valued equality result'


class SelfResultEq:
    def __eq__(self, other):
        return self


self_result_eq = SelfResultEq()
assert (self_result_eq == 1) is self_result_eq, 'direct equality preserves a self result'
assert self_result_eq in [1], 'container membership truth-tests a self result'

# === Instances are always truthy ===
assert bool(p) is True
if q:
    pass
else:
    assert False, 'instance should be truthy in a condition'

# === Bound methods ===
m = p.total
assert m() == 105
move = p.move
move(10, 10)
assert p.x == 110 and p.y == 15, 'bound method with arguments mutates the instance'

# === getattr() / hasattr() ===
assert getattr(p, 'x') == 110
assert getattr(p, 'total')() == 125
assert getattr(p, 'nope', 'default') == 'default'
assert hasattr(p, 'x')
assert hasattr(p, 'total')
assert not hasattr(p, 'nope'), 'hasattr false for a missing attribute'

# === A class with no __init__ ===


class Empty:
    pass


e = Empty()
assert type(e) is Empty
assert type(e).__name__ == 'Empty'
assert isinstance(e, Empty)

# === A class whose only members are methods ===


class Counter:
    def __init__(self) -> None:
        self.n = 0

    def inc(self) -> None:
        self.n += 1

    def get(self) -> int:
        return self.n


c = Counter()
c.inc()
c.inc()
c.inc()
assert c.get() == 3

# === Error cases ===
try:
    e.nope
    assert False, 'expected AttributeError for missing attribute'
except AttributeError as exc:
    assert str(exc) == "'Empty' object has no attribute 'nope'"

try:
    e.nope()
    assert False, 'expected AttributeError for missing method'
except AttributeError as exc:
    assert str(exc) == "'Empty' object has no attribute 'nope'"

try:
    Empty(1)
    assert False, 'expected TypeError when passing args to a class with no __init__'
except TypeError as exc:
    assert str(exc) == 'Empty() takes no arguments'

# === Exception raised inside __init__ propagates (and the half-built instance
# is cleaned up — checked under memory-model-checks) ===


class Boom:
    def __init__(self, x: int) -> None:
        self.x = x
        raise ValueError('boom')


try:
    Boom(1)
    assert False, 'expected ValueError from __init__'
except ValueError as exc:
    assert str(exc) == 'boom'

# === Reference cycles between instances are reclaimable (exercises GC tracing
# of Instance children) ===


class Link:
    def __init__(self) -> None:
        self.other = None


n1 = Link()
n2 = Link()
n1.other = n2
n2.other = n1  # cycle: n1 <-> n2
assert n1.other.other is n1

# Self reference.
n1.other = n1
assert n1.other is n1

# === Bound methods hash by identity: the same bound-method object works as a
# dict key (CPython hashes by (instance, func); see limitations/classes.md) ===

m = c.inc
d = {m: 'inc'}
assert d[m] == 'inc'
assert hash(m) == hash(m)
s = {m, m}
assert len(s) == 1

# === A name bound more than once in the class body: last binding wins, the
# replaced (heap-allocated) value is released ===


class Rebound:
    items = [1]
    items = [2, 3]


assert Rebound.items == [2, 3]

# === Exotic __init__ members: CPython's type.__call__ looks __init__ up with
# descriptor binding, so only plain functions bind the new instance as self;
# anything else is called with the constructor args unchanged and must still
# return None ===


class _Helper:
    def __init__(self, x=None):
        self.x = x


class InitIsClass:
    __init__ = _Helper


try:
    InitIsClass()
    assert False, 'expected InitIsClass() to raise'
except TypeError as e:
    assert str(e) == "__init__() should return None, not '_Helper'"


class InitNotCallable:
    __init__ = 42


try:
    InitNotCallable()
    assert False, 'expected InitNotCallable() to raise'
except TypeError as e:
    assert str(e) == "'int' object is not callable"


class InitReturnsValue:
    def __init__(self):
        return 'nope'


try:
    InitReturnsValue()
    assert False, 'expected InitReturnsValue() to raise'
except TypeError as e:
    assert str(e) == "__init__() should return None, not 'str'"


class InitAsync:
    async def __init__(self):
        pass


try:
    InitAsync()
    assert False, 'expected InitAsync() to raise'
except TypeError as e:
    assert str(e) == "__init__() should return None, not 'coroutine'"


# A builtin __init__ that returns None: the instance is constructed and the
# builtin receives only the constructor args (no self).
class InitBuiltin:
    __init__ = print


ib = InitBuiltin('init-builtin-arg')
assert type(ib) is InitBuiltin


# A bound method used as __init__ keeps its own receiver; the new instance is
# not prepended.
class Recorder:
    def __init__(self):
        self.calls = []

    def record(self, *args):
        self.calls.append(args)


rec = Recorder()


class InitBoundMethod:
    __init__ = rec.record


ibm = InitBoundMethod(1, 2)
assert type(ibm) is InitBoundMethod
assert rec.calls == [(1, 2)]


# === `...` as the class body (common stub idiom) ===
class Stub: ...


s = Stub()
assert type(s) is Stub
s.x = 1
assert s.x == 1