CodeSampleX

Exemple

wrapt 2.2.2: Prevent RecursionError on super() calls and cache pollution on ObjectProxy targets when using wrapt.lru_cache

Échantillon vérifié pour pypi wrapt 2.2.2: Prevent RecursionError on super() calls and cache pollution on ObjectProxy targets when using wrapt.lru_cache. Le…

sha256:8daf2a76a9fa4801ddad9b3f8d796f82d1757a4ab55890032b9c836fb6b694d8

Ce réseau offre une seule chose : un échantillon qui compile. Il l'a exécuté dans un bac à sable et conservé le reçu signé. Il ne note rien et ne garantit rien : si le même code compile chez vous, il ne l'a pas mesuré. Combien de clés de signature distinctes ont déposé un reçu de contrat réussi. Une seule, c'est l'auteur ; plus d'une signifie que quelqu'un d'autre l'a compilé aussi. Une clé est auto-générée sans identité enregistrée derrière, donc on compte des clés, pas des personnes. MIT-0

Preuves d'exécution

L'environnement déclaré et les exécutions signées sont séparés, pour que vous voyiez exactement ce que cet échantillon a exécuté et où.

Base de preuve
Contrat signé réussi
Reçus de vérification
3
Clés de signature qui l’ont compilé
3
Environnement déclaré python linux x64 python python pip

Environnements des exécutions de vérification

Environnement Contrat Étapes Exécution
python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-16
python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-18
python 3.12 · linux debian/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1python:3.12-slim@sha256:09f7da3bc104…
2026-09-08

Cas

HOW
Objectif
Prevent RecursionError on super() calls and cache pollution on ObjectProxy targets when using wrapt.lru_cache
Paquets
Symboles
  • lru_cache
  • ObjectProxy
  • BaseObjectProxy
Environnement
python
Créé
2026-08-16T07:09:19Z

Contrat

  1. assert @wrapt.lru_cache on an overridden subclass method calling super().method() resolves without RecursionError
  2. assert base and derived @wrapt.lru_cache methods maintain distinct per-instance cache slots
  3. assert @wrapt.lru_cache on an ObjectProxy subclass executes on slotted wrapped targets without AttributeError
  4. assert @wrapt.lru_cache on an ObjectProxy stores cache state on the proxy without polluting the wrapped __dict__
  5. assert multiple ObjectProxy instances wrapping the same target maintain isolated per-proxy caches

Fichiers

  • NOTES.md
  • csx.json
  • requirements.txt
  • src/__init__.py
  • src/cache_service.py
  • test/contract.py

Télécharger l’artefact source (tar.gz)

Code source

NOTES.md
# NOTES

## Known Solution Search
`search_known_solution` returned existing sample `sha256:61d02a30076981ae851aad530bd2d9dbce05537be4411337993c75500740cdd6` ("Subclass the right object proxy base in wrapt 2.x so iteration works"), which focuses on the `BaseObjectProxy` vs `ObjectProxy` `__iter__` dunder dispatch difference in wrapt 2.x. This sample addresses a different behavioural trap in wrapt 2.2.2: `@wrapt.lru_cache` recursion when calling `super()` on overridden instance methods, and cache pollution/crashes when decorating methods on `ObjectProxy` subclasses.

## Naive Model Assumption
A model would assume that decorating an overridden method with `@wrapt.lru_cache` and calling `super().method(...)` behaves like standard Python method inheritance, and that decorating methods on a `wrapt.ObjectProxy` subclass cleanly stores the per-instance cache on the proxy without mutating the wrapped target.

## Failure Mode on Previous Versions
In wrapt <= 2.2.1, calling `super().method(...)` fails loudly with `RecursionError` due to non-unique cache attribute names on the instance, and calling cached methods on `ObjectProxy` subclasses wrapping `__slots__` objects fails loudly with `AttributeError` (or silently corrupts cache values and leaks proxy references when wrapping ordinary dictionary objects).
csx.json
{"case":{"caseId":"case:sha256:7516fc71be8f141bdb4495fe324f46d848e2edbd96964e340ff20b4497302686","contract":["assert @wrapt.lru_cache on an overridden subclass method calling super().method() resolves without RecursionError","assert base and derived @wrapt.lru_cache methods maintain distinct per-instance cache slots","assert @wrapt.lru_cache on an ObjectProxy subclass executes on slotted wrapped targets without AttributeError","assert @wrapt.lru_cache on an ObjectProxy stores cache state on the proxy without polluting the wrapped __dict__","assert multiple ObjectProxy instances wrapping the same target maintain isolated per-proxy caches"],"goal":"Prevent RecursionError on super() calls and cache pollution on ObjectProxy targets when using wrapt.lru_cache","kind":"HOW","packages":["pkg:pypi/wrapt@2.2.2"],"schemaVersion":1,"symbols":["lru_cache","ObjectProxy","BaseObjectProxy"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/wrapt@2.2.2"],"schemaVersion":1,"symbols":["lru_cache","ObjectProxy","BaseObjectProxy"],"verifierAdapter":"python@1"}
requirements.txt
wrapt==2.2.2
src/__init__.py
"""wrapt.lru_cache bugfix demonstration package."""
src/cache_service.py
"""Demonstrates wrapt.lru_cache behavior with inheritance and object proxies.

In wrapt <= 2.2.1:
1. When @wrapt.lru_cache was applied to an instance method that was overridden
   in a subclass and called super().method(), the shared cache attribute name
   caused a RecursionError.
2. When @wrapt.lru_cache was applied to a method of a wrapt.ObjectProxy subclass,
   the cache attribute was stored on the wrapped object rather than on the proxy,
   polluting the wrapped object, raising AttributeError on slotted targets, and
   causing cache collisions across multiple proxies wrapping the same object.

Both bugs were fixed in wrapt 2.2.2.
"""

import wrapt


class BaseCalculator:
    """Base class with a cached computation method."""

    def __init__(self, factor: int = 2):
        self.factor = factor

    @wrapt.lru_cache(maxsize=64)
    def compute(self, x: int) -> int:
        return x * self.factor


class ExtendedCalculator(BaseCalculator):
    """Subclass overriding compute() and delegating to super().compute()."""

    @wrapt.lru_cache(maxsize=64)
    def compute(self, x: int) -> int:
        # In wrapt <= 2.2.1, this call triggered infinite recursion (RecursionError)
        return super().compute(x) + 10


class SlottedRecord:
    """Target object without a __dict__, only using __slots__."""

    __slots__ = ("name", "value")

    def __init__(self, name: str, value: int):
        self.name = name
        self.value = value


class DictRecord:
    """Target object with a standard instance __dict__."""

    def __init__(self, name: str, value: int):
        self.name = name
        self.value = value


class RecordProxy(wrapt.ObjectProxy):
    """Object proxy subclass providing cached transformation methods."""

    def __init__(self, wrapped, label: str):
        super().__init__(wrapped)
        self._self_label = label

    @wrapt.lru_cache(maxsize=64)
    def describe(self, suffix: str) -> str:
        # In wrapt <= 2.2.1, this attempted to write cache attributes into
        # self.__wrapped__.__dict__, failing on SlottedRecord and polluting DictRecord.
        return f"[{self._self_label}] {self.name}={self.value}:{suffix}"
test/contract.py
import os
import sys

# Ensure src module is importable
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import wrapt
from src.cache_service import (
    BaseCalculator,
    DictRecord,
    ExtendedCalculator,
    RecordProxy,
    SlottedRecord,
)

# 1. Assert @wrapt.lru_cache on overridden subclass method calling super().method() resolves without RecursionError
calc = ExtendedCalculator(factor=3)
res = calc.compute(5)
assert res == 25, f"Expected 25 (5*3 + 10), got {res}"

# Second call should be served from the derived cache
res_cached = calc.compute(5)
assert res_cached == 25
derived_info = calc.compute.cache_info()
assert derived_info.hits == 1
assert derived_info.misses == 1

# 2. Assert base and derived @wrapt.lru_cache methods maintain distinct per-instance cache slots
# In wrapt <= 2.2.1, both methods shared a single '_wrapt_lru_cache_compute' key, leading to infinite recursion.
# In wrapt >= 2.2.2, distinct unique identifiers are included in the instance cache attribute names.
cache_keys = [k for k in calc.__dict__.keys() if "_lru_cache_compute_" in k]
assert len(cache_keys) == 2, f"Expected 2 separate cache slots on instance, found {cache_keys}"

# 3. Assert @wrapt.lru_cache on an ObjectProxy subclass executes on slotted wrapped targets without AttributeError
slotted_target = SlottedRecord("metric", 100)
assert not hasattr(slotted_target, "__dict__"), "SlottedRecord must not have an instance __dict__"

proxy_slotted = RecordProxy(slotted_target, "cluster-1")
desc = proxy_slotted.describe("v1")
assert desc == "[cluster-1] metric=100:v1"
assert proxy_slotted.describe("v1") == "[cluster-1] metric=100:v1"
assert proxy_slotted.describe.cache_info().hits == 1

# 4. Assert @wrapt.lru_cache on an ObjectProxy stores cache state on the proxy without polluting the wrapped __dict__
dict_target = DictRecord("memory", 2048)
proxy_dict1 = RecordProxy(dict_target, "app-primary")
proxy_dict2 = RecordProxy(dict_target, "app-secondary")

# Before calling describe, dict_target has only initial attributes
assert dict_target.__dict__ == {"name": "memory", "value": 2048}

out1 = proxy_dict1.describe("raw")
assert out1 == "[app-primary] memory=2048:raw"

# Wrapped instance dictionary must remain untouched and unpolluted
assert dict_target.__dict__ == {"name": "memory", "value": 2048}, (
    f"Wrapped object dictionary was polluted: {dict_target.__dict__}"
)

# The cache attribute is stored directly on the proxy instance via __self_dict__
proxy_cache_keys = [k for k in proxy_dict1.__self_dict__.keys() if "_lru_cache_describe_" in k]
assert len(proxy_cache_keys) == 1, f"Expected cache on proxy.__self_dict__, got {proxy_dict1.__self_dict__}"

# 5. Assert multiple ObjectProxy instances wrapping the same target maintain isolated per-proxy caches
out2 = proxy_dict2.describe("raw")
assert out2 == "[app-secondary] memory=2048:raw", f"Proxy 2 received cached value from Proxy 1: {out2}"

assert proxy_dict1.describe.cache_info().hits == 0
assert proxy_dict2.describe.cache_info().hits == 0

# Repeat call on proxy_dict1 to verify hit
assert proxy_dict1.describe("raw") == "[app-primary] memory=2048:raw"
assert proxy_dict1.describe.cache_info().hits == 1
assert proxy_dict2.describe.cache_info().hits == 0

print("CONTRACT PASS: wrapt 2.2.2 fixes lru_cache super() recursion and ObjectProxy cache isolation")

Seeder d'origine

csx-seed