Ejemplo
wrapt 2.3.0: Subclass the right object proxy base in wrapt 2.x so iteration works
Muestra verificada para pypi wrapt 2.3.0: Subclass the right object proxy base in wrapt 2.x so iteration works. El contrato se ejecutó en python 3.12 · linux…
sha256:61d02a30076981ae851aad530bd2d9dbce05537be4411337993c75500740cdd6
Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido.
Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas.
MIT-0
Evidencia de ejecución
El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.
- Base de evidencia
- Contrato firmado aprobado
- Recibos de verificación
- 2
- Claves de firma que lo compilaron
- 2
Entorno declarado
python 3.12 linux x64 python 3.12 python pip
Entornos de las ejecuciones de verificación
| Entorno | Contrato | Etapas | Ejecución |
|---|---|---|---|
| python 3.12 · linux alpine/x64 · docker ed25519:a2ec939a4c60e243 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-14 |
| python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-18 |
Caso
HOW- Objetivo
- Subclass the right object proxy base in wrapt 2.x so iteration works
- Paquetes
- Símbolos
-
- BaseObjectProxy
- ObjectProxy
- AutoObjectProxy
- with_signature
- function_wrapper
- Entorno
- python 3.12
- Creado
- 2026-08-14T20:44:52Z
Contrato
- assert ObjectProxy is a subclass of BaseObjectProxy, not an alias for it
- assert BaseObjectProxy around a list refuses to iterate while isinstance and hasattr both claim it is iterable
- assert the 1.x shape of subclassing ObjectProxy still iterates, but reports a non-iterable as iterable
- assert AutoObjectProxy agrees with reality both ways and builds a new class per instance
- assert wrapt.signature is a module so calling it raises TypeError, while inspect.signature reads the original signature through a wrapt decorator
- assert wrapt.function_wrapper is not wrapt.decorator and takes no enabled/adapter/proxy keywords
- assert proxy subclass state needs the _self_ prefix or it leaks onto the wrapped object
Archivos
- csx.json
- requirements.txt
- src/__init__.py
- src/compat.py
- test/contract.py
Código fuente
{"case":{"caseId":"case:sha256:ef4e9a72558ecec67503f11767568b8fada3a15ab81f2ce0f46e5ec34fe75d6e","constraints":{"runtime":"python"},"contract":["assert ObjectProxy is a subclass of BaseObjectProxy, not an alias for it","assert BaseObjectProxy around a list refuses to iterate while isinstance and hasattr both claim it is iterable","assert the 1.x shape of subclassing ObjectProxy still iterates, but reports a non-iterable as iterable","assert AutoObjectProxy agrees with reality both ways and builds a new class per instance","assert wrapt.signature is a module so calling it raises TypeError, while inspect.signature reads the original signature through a wrapt decorator","assert wrapt.function_wrapper is not wrapt.decorator and takes no enabled/adapter/proxy keywords","assert proxy subclass state needs the _self_ prefix or it leaks onto the wrapped object"],"goal":"Subclass the right object proxy base in wrapt 2.x so iteration works","kind":"HOW","packages":["pkg:pypi/wrapt@2.3.0"],"schemaVersion":1,"symbols":["BaseObjectProxy","ObjectProxy","AutoObjectProxy","with_signature","function_wrapper"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","runtimeVersion":"3.12","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/wrapt@2.3.0"],"schemaVersion":1,"symbols":["BaseObjectProxy","ObjectProxy","AutoObjectProxy","with_signature","function_wrapper"],"verifierAdapter":"python@1"}
wrapt==2.3.0
"""Picking an object-proxy base class in wrapt 2.x.
wrapt sat on 1.x from 2013 until August 2025, so essentially every example in
circulation says "subclass wrapt.ObjectProxy". In 2.x that name still exists and
still imports, which is exactly why the change is easy to miss: ObjectProxy is
no longer the base class. The base is BaseObjectProxy, and ObjectProxy is a thin
backwards-compatibility subclass whose only added special method is __iter__.
Which class you pick decides whether iteration works, and the usual duck-typing
checks cannot tell you which one you have: isinstance() and hasattr() both
answer "iterable" for the two combinations where iteration actually fails.
"""
import collections.abc as abc
import wrapt
# The C-level base type in 2.x (wrapt.BaseObjectProxy, module "_wrappers").
# wrapt's own ObjectProxy docstring points new subclasses here: "If you don't
# need backward compatibility for __iter__() support then it is preferable to
# use BaseObjectProxy directly."
BASE = wrapt.BaseObjectProxy
# The 1.x-compatible name (module "wrapt.proxies"). It is a SUBCLASS of BASE,
# not an alias for it, and the only special method it adds is __iter__.
COMPAT = wrapt.ObjectProxy
# Builds a bespoke class per instance carrying the dunder methods the wrapped
# object actually has. Correct, but it pays a new class for every proxy created.
AUTO = wrapt.AutoObjectProxy
def can_iterate(proxy):
"""Whether the proxy can ACTUALLY be consumed as an iterable.
Python resolves __iter__ on the type and never on the instance, so a proxy
iterates only when its own class defines __iter__. wrapt's __getattr__
forwarding does not help here. The consequence that catches people out:
list(BaseObjectProxy([1, 2, 3])) raises TypeError even though the wrapped
object is a list.
"""
try:
list(proxy)
return True
except TypeError:
return False
def looks_iterable(proxy):
"""The cheap pre-check an agent reaches for -- and it over-reports.
isinstance() against an ABC consults the proxy's __class__, which is
forwarded to the WRAPPED object, and also the proxy's real type; iteration
is decided by the real type alone. So "if isinstance(p, Iterable): list(p)"
is the obvious wrong version, and it is wrong the SAME way both times --
a false positive, for two different reasons. BASE around a list says True
because the wrapped list is iterable; COMPAT around a non-iterable says
True because the proxy type defines __iter__. Both then raise on list(p).
"""
return isinstance(proxy, abc.Iterable)
import inspect
import os
import sys
import types
import collections.abc as abc
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import wrapt
from src.compat import AUTO, BASE, COMPAT, can_iterate, looks_iterable
class Bare:
"""A plain object with no dunder methods beyond the defaults."""
def __init__(self):
self.value = 1
# ObjectProxy is NOT the base class in 2.x, and not an alias for it either: it
# is a subclass of the real base, and so is AutoObjectProxy.
assert wrapt.ObjectProxy is not wrapt.BaseObjectProxy
assert issubclass(wrapt.ObjectProxy, wrapt.BaseObjectProxy)
assert issubclass(wrapt.AutoObjectProxy, wrapt.BaseObjectProxy)
# The base class does not DEFINE __iter__ on the type, and that is where Python
# looks for it, so a proxied list will not iterate: the obvious "list(proxy)"
# raises TypeError even though the wrapped object is a list. Attribute
# forwarding is irrelevant here -- this is the whole reason ObjectProxy stayed.
base_list = BASE([1, 2, 3])
assert can_iterate(base_list) is False, "BaseObjectProxy unexpectedly iterated"
# ...while every non-dunder-dispatched check insists it is fine.
assert looks_iterable(base_list) is True
assert hasattr(base_list, "__iter__") is True
assert base_list.__class__ is list
assert len(base_list) == 3 and base_list[0] == 1
# Calling the forwarded attribute by hand does work; only the iter() protocol,
# which looks the method up on the type, fails.
assert list(base_list.__iter__()) == [1, 2, 3]
# The compat class iterates, because __iter__ is precisely what it adds. That
# is why 1.x-shaped code -- "class MyProxy(wrapt.ObjectProxy)" -- keeps running
# unchanged, and why nothing announces that a different base is now intended.
assert can_iterate(COMPAT([1, 2, 3])) is True
class LegacyProxy(COMPAT):
"""Exactly what twelve years of wrapt 1.x examples tell you to write."""
assert list(LegacyProxy([1, 2, 3])) == [1, 2, 3]
# But it adds __iter__ unconditionally, so it claims a NON-iterable is iterable.
compat_bare = COMPAT(Bare())
assert looks_iterable(compat_bare) is True, "expected the false positive"
assert can_iterate(compat_bare) is False
# The base class gets this one right: the false positive changes sides with the
# base class you picked, so neither answer can be trusted on its own.
assert looks_iterable(BASE(Bare())) is False
# AutoObjectProxy is the only one that agrees with reality in both directions.
assert can_iterate(AUTO([1, 2, 3])) is True
assert looks_iterable(AUTO(Bare())) is False
assert isinstance(AUTO([1, 2, 3]), abc.Iterable) is True
# The cost is a freshly built class for every instance, so identity checks on
# type() and any per-class caching keyed on it stop working.
assert type(AUTO([1, 2])) is not type(AUTO([3, 4]))
assert type(COMPAT([1, 2])) is type(COMPAT([3, 4]))
# wrapt.signature is a MODULE, not a callable. "wrapt.signature(fn)" reads like
# a proxy-aware inspect.signature and fails at runtime instead; the exported
# helper is wrapt.with_signature, and plain inspect.signature already sees
# through a wrapt decorator.
assert isinstance(wrapt.signature, types.ModuleType)
try:
wrapt.signature(lambda a: a)
raise AssertionError("expected TypeError: module object is not callable")
except TypeError:
pass
assert callable(wrapt.with_signature)
@wrapt.decorator
def passthrough(wrapped, instance, args, kwargs):
return wrapped(*args, **kwargs)
@passthrough
def add(a, b=2):
return a + b
# The reason you do not need a proxy-aware signature helper: the decorated
# function still advertises the ORIGINAL signature, not (*args, **kwargs).
assert str(inspect.signature(add)) == "(a, b=2)"
assert add(1) == 3
# Likewise wrapt.function_wrapper is not another name for wrapt.decorator: it
# takes the wrapper and nothing else, so the enabled/adapter/proxy options exist
# on decorator only.
assert wrapt.function_wrapper is not wrapt.decorator
assert list(inspect.signature(wrapt.function_wrapper).parameters) == ["wrapper"]
assert list(inspect.signature(wrapt.decorator).parameters) == [
"wrapper",
"enabled",
"adapter",
"proxy",
]
# Subclass state still needs the _self_ prefix on the 2.x base: any other name
# is written through onto the wrapped object rather than kept on the proxy.
class Counter(BASE):
def __init__(self, wrapped):
super().__init__(wrapped)
self._self_hits = 0
def bump(self):
self._self_hits += 1
return self._self_hits
counter = Counter(Bare())
counter.bump()
assert counter.bump() == 2
assert not hasattr(counter.__wrapped__, "_self_hits")
class Leaky(BASE):
def __init__(self, wrapped):
super().__init__(wrapped)
self.hits = 0
leaky = Leaky(Bare())
assert leaky.__wrapped__.hits == 0, "plain attribute should leak to the wrapped object"
print("CONTRACT PASS: wrapt 2.x proxy bases differ on __iter__ and isinstance lies")