-
Notifications
You must be signed in to change notification settings - Fork 19
Fold a virtual's never-stored field read, check its slot descr, and carry surrogate-bearing text as WTF-8 #1089
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1bce179
3faf25f
c9ea8fb
71ea0ac
1117c33
0da31b8
d4e3d7f
6134a52
97216da
ff8b448
cc21631
463c905
ffc4291
b10583b
a40082a
96c86bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,137 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """A devolved instance dictionary probed by attribute name. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The probe compares the name against whatever each colliding bucket holds, so a | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stored non-string key can reach a user `__eq__`. When that raises, the read | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| must propagate the exception; reporting it as a miss would silently let a class | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attribute of the same name answer instead. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| The lone-surrogate blocks exercise the other half of the name dispatch: a name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| that is not valid UTF-8 has no borrowed-str view, so it wraps before probing. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class Colliding: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Hashes as `"zz"` and refuses to compare.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __hash__(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return hash("zz") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __eq__(self, other): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError("boom") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class Quiet: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Hashes as `"zz"` and compares unequal without raising.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __hash__(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return hash("zz") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __eq__(self, other): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NotImplemented | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def devolve(obj): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """Grow the instance dict past the mapdict limit, then return it.""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for i in range(200): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setattr(obj, "a%d" % i, i) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return obj.__dict__ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class R: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| zz = "CLASSVALUE" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # A raising comparison in the probe surfaces, and the class attribute does not | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # win by default. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| r = R() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| devolve(r)[Colliding()] = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| r.zz | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except ValueError as exc: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert str(exc) == "boom", str(exc) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise AssertionError("a raising __eq__ in the probe was reported as a miss") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # getattr and __getattribute__ reach the same probe. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for read in (lambda o: getattr(o, "zz"), lambda o: type(o).__getattribute__(o, "zz")): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| read(r) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except ValueError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise AssertionError("raising __eq__ swallowed on an alternate read path") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # The dict subscript itself agrees. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| r.__dict__["zz"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except ValueError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except KeyError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise AssertionError("raising __eq__ reported as a missing key") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+65
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Two "must raise" blocks accept a successful read. Both blocks assert that a raising
💚 Proposed fix for both blocks try:
r.__dict__["zz"]
except ValueError:
pass
except KeyError:
raise AssertionError("raising __eq__ reported as a missing key")
+else:
+ raise AssertionError("raising __eq__ swallowed on the dict subscript") try:
getattr(s2, SURROGATE)
except ValueError:
pass
except AttributeError:
raise AssertionError("raising __eq__ reported as a missing attribute")
+else:
+ raise AssertionError("raising __eq__ swallowed on a surrogate name")📝 Committable suggestion
Suggested change
Suggested change
🧰 Tools🪛 Ruff (0.16.1)[warning] 70-70: Within an (B904) [warning] 70-70: Avoid specifying long messages outside the exception class (TRY003) 📍 Affects 1 file
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # A colliding key that compares unequal without raising is an ordinary miss, so | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # the class attribute answers. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| q = R() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| devolve(q)[Quiet()] = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert q.zz == "CLASSVALUE" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # An instance attribute still wins over the class attribute after devolving. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| own = R() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| devolve(own) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| own.zz = "OWN" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert own.zz == "OWN" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| own.__dict__[Quiet()] = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert own.zz == "OWN" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # A builtin subclass reaches the same terminator. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import _random # noqa: E402 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class Rand(_random.Random): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| zz = "CLASSVALUE" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rand = Rand() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| devolve(rand)[Colliding()] = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rand.zz | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except ValueError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise AssertionError("raising __eq__ swallowed on a builtin subclass") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # A lone-surrogate attribute name takes the wrapping arm of the name dispatch. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SURROGATE = "z\udcffz" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class S: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| s = S() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| devolve(s) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setattr(s, SURROGATE, "SURR") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert getattr(s, SURROGATE) == "SURR" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert s.__dict__[SURROGATE] == "SURR" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ... and it propagates a raising comparison too. `Colliding` hashes as "zz", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # so pick a colliding key for this name instead. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class CollidingSurrogate: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __hash__(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return hash(SURROGATE) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def __eq__(self, other): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise ValueError("boom") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| s2 = S() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| devolve(s2) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| s2.__dict__[CollidingSurrogate()] = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getattr(s2, SURROGATE) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except ValueError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except AttributeError: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise AssertionError("raising __eq__ reported as a missing attribute") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print("OK") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In release builds,
field_slot_disagreementis disabled, sooptimize_setfield_gccan still store a value atfield_idxfor a mismatched descriptor; this new arm then deliberately ignores that tracked value, causing a subsequentGETFIELD_GCwith the same descriptor to return zero rather than the value just stored. The concretevable_tokendescriptor currently has the placeholder index 0, so a virtual frame that encounters both its token store and read can hit this inconsistency. Assign the appended field its correct generated descriptor/index and retain the normal upstream getfield path rather than masking descriptor-generation defects with a general zero-fold shortcut.AGENTS.md reference: AGENTS.md:L231-L233
Useful? React with 👍 / 👎.