Skip to content

Commit 47a4068

Browse files
Make sure proxies are slotted properly
1 parent d414db0 commit 47a4068

4 files changed

Lines changed: 42 additions & 5 deletions

File tree

observ/dict_proxy.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151

5252

5353
class DictProxyBase(Proxy[dict]):
54+
__slots__ = ()
55+
5456
def _orphaned_keydeps(self):
5557
return set(proxy_db.attrs(self)["keydep"].keys()) - set(self.__target__.keys())
5658

@@ -64,12 +66,16 @@ def readonly_dict_proxy_init(self, target, shallow=False, **kwargs):
6466
DictProxy = type(
6567
"DictProxy",
6668
(DictProxyBase,),
67-
construct_methods_traps_dict(dict, dict_traps, trap_map),
69+
{
70+
"__slots__": (),
71+
**construct_methods_traps_dict(dict, dict_traps, trap_map),
72+
},
6873
)
6974
ReadonlyDictProxy = type(
7075
"ReadonlyDictProxy",
7176
(DictProxyBase,),
7277
{
78+
"__slots__": (),
7379
"__init__": readonly_dict_proxy_init,
7480
**construct_methods_traps_dict(dict, dict_traps, trap_map_readonly),
7581
},

observ/list_proxy.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646

4747
class ListProxyBase(Proxy[list]):
48-
pass
48+
__slots__ = ()
4949

5050

5151
def readonly_list_proxy_init(self, target, shallow=False, **kwargs):
@@ -57,12 +57,16 @@ def readonly_list_proxy_init(self, target, shallow=False, **kwargs):
5757
ListProxy = type(
5858
"ListProxy",
5959
(ListProxyBase,),
60-
construct_methods_traps_dict(list, list_traps, trap_map),
60+
{
61+
"__slots__": (),
62+
**construct_methods_traps_dict(list, list_traps, trap_map),
63+
},
6164
)
6265
ReadonlyListProxy = type(
6366
"ReadonlyListProxy",
6467
(ListProxyBase,),
6568
{
69+
"__slots__": (),
6670
"__init__": readonly_list_proxy_init,
6771
**construct_methods_traps_dict(list, list_traps, trap_map_readonly),
6872
},

observ/set_proxy.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454

5555

5656
class SetProxyBase(Proxy[set]):
57-
pass
57+
__slots__ = ()
5858

5959

6060
def readonly_set_proxy_init(self, target, shallow=False, **kwargs):
@@ -64,12 +64,15 @@ def readonly_set_proxy_init(self, target, shallow=False, **kwargs):
6464

6565

6666
SetProxy = type(
67-
"SetProxy", (SetProxyBase,), construct_methods_traps_dict(set, set_traps, trap_map)
67+
"SetProxy",
68+
(SetProxyBase,),
69+
{"__slots__": (), **construct_methods_traps_dict(set, set_traps, trap_map)},
6870
)
6971
ReadonlySetProxy = type(
7072
"ReadonlysetProxy",
7173
(SetProxyBase,),
7274
{
75+
"__slots__": (),
7376
"__init__": readonly_set_proxy_init,
7477
**construct_methods_traps_dict(set, set_traps, trap_map_readonly),
7578
},

tests/test_proxy.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,27 @@ class Custom(dict):
267267
assert not isinstance(p, Proxy)
268268
assert isinstance(raw, dict)
269269
assert p is raw
270+
271+
272+
def test_proxy_is_slotted():
273+
some_dict = proxy({1: 1, 2: 2, 3: 3})
274+
assert isinstance(some_dict, DictProxy)
275+
some_list = proxy([1, 2, 3])
276+
assert isinstance(some_list, ListProxy)
277+
some_set = proxy({1, 2, 3})
278+
assert isinstance(some_set, SetProxy)
279+
280+
with pytest.raises(
281+
AttributeError, match="'DictProxy' object has no attribute 'foo'"
282+
):
283+
some_dict.foo = "foo"
284+
285+
with pytest.raises(
286+
AttributeError, match="'ListProxy' object has no attribute 'foo'"
287+
):
288+
some_list.foo = "foo"
289+
290+
with pytest.raises(
291+
AttributeError, match="'SetProxy' object has no attribute 'foo'"
292+
):
293+
some_set.foo = "foo"

0 commit comments

Comments
 (0)