Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Optimiser breaks attribute lookup from metaclass property #94822

Closed
Assignees
brandtbucher
Labels
3.11only security fixes3.12only security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error
@oscarbenjamin

Description

@oscarbenjamin

Bug report

This comes from a SymPy issue:sympy/sympy#23774

It looks like there is a nondeterministic error in the optimisation introduced in96346cb from#27722 first included in CPython version 3.11.0a1. The optimisation speeds up calling methods but sometimes retrieves the wrong attribute from a class whose metaclass defines a property method.

The way that this arises is quite sensitive to small changes so I haven't been able to distil a standalone reproducer. I'll show how to reproduce this using SymPy below but first this is a simplified schematic of the situation:

classMetaA(type):def__init__(cls,*args,**kws):passclassA(metaclass=MetaA):defmethod(self,rule):return'A method'classMetaB(MetaA):@propertydefmethod(self):defmethod_inner(rule):return'MetaB function'returnmethod_innerclassB(A,metaclass=MetaB):passprint(B.method(1))# MetaB functionprint(B().method(1))# A method

HereB is a subclass ofA but an instance ofMetaB. Both definemethod but theMetaB method should be used when accessed from the classB rather than an instanceB(). The failure seen in SymPy is that sometimesB.method(1) will execute asA.method(1) which fails because of the missingself argument.

The following code reproduces the problem and fails something like 50% of the time with SymPy 1.10.1 and CPython 3.11.0a1-3.11.0b4:

fromsympyimport*x,y=symbols('x, y')f=Function('f')# These two lines look irrelevant but are needed:expr=sin(x*exp(y))Derivative(expr,y).subs(y,x).doit()expr=Subs(Derivative(f(f(x)),x),f,cos)# This is where it blows up:expr.doit()

The failure is not deterministic:

$python bug.py$python bug.py$python bug.pyTraceback (most recent call last):  File "/home/oscar/current/sympy/sympy.git/bug.py", line 13, in <module>    expr.doit()    ^^^^^^^^^^^  File "/home/oscar/current/sympy/sympy.git/sympy/core/function.py", line 2246, in doit    e = e.subs(vi, p[i])        ^^^^^^^^^^^^^^^^  File "/home/oscar/current/sympy/sympy.git/sympy/core/basic.py", line 997, in subs    rv = rv._subs(old, new, **kwargs)         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File "/home/oscar/current/sympy/sympy.git/sympy/core/cache.py", line 70, in wrapper    retval = cfunc(*args, **kwargs)             ^^^^^^^^^^^^^^^^^^^^^^  File "/home/oscar/current/sympy/sympy.git/sympy/core/basic.py", line 1109, in _subs    rv = self._eval_subs(old, new)         ^^^^^^^^^^^^^^^^^^^^^^^^^  File "/home/oscar/current/sympy/sympy.git/sympy/core/function.py", line 1737, in _eval_subs    nfree = new.xreplace(syms).free_symbols            ^^^^^^^^^^^^^^^^^^TypeError: Basic.xreplace() missing 1 required positional argument: 'rule'

The failure can be reproduced deterministically by setting the hash seed:

$PYTHONHASHSEED=1 python bug.py...TypeError: Basic.xreplace() missing 1 required positional argument: 'rule'

In the debugger the same code that already failed succeeds:

$PYTHONHASHSEED=1 python -m pdb bug.py>/home/oscar/current/sympy/sympy.git/bug.py(1)<module>()-> from sympy import *(Pdb) cTraceback (most recent call last):  File "/media/oscar/EXT4_STUFF/src/cpython/Lib/pdb.py", line 1768, in main    pdb._run(target)  File "/media/oscar/EXT4_STUFF/src/cpython/Lib/pdb.py", line 1646, in _run    self.run(target.code)  File "/media/oscar/EXT4_STUFF/src/cpython/Lib/bdb.py", line 597, in run    exec(cmd, globals, locals)  File "<string>", line 1, in <module>  File "/home/oscar/current/sympy/sympy.git/bug.py", line 13, in <module>    expr.doit()  File "/home/oscar/current/sympy/sympy.git/sympy/core/function.py", line 2255, in doit    e = e.subs(vi, p[i])        ^^^^^^^^^^^^^^^^  File "/home/oscar/current/sympy/sympy.git/sympy/core/basic.py", line 993, in subs    rv = rv._subs(old, new, **kwargs)         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File "/home/oscar/current/sympy/sympy.git/sympy/core/cache.py", line 70, in wrapper    retval = cfunc(*args, **kwargs)             ^^^^^^^^^^^^^^^^^^^^^^  File "/home/oscar/current/sympy/sympy.git/sympy/core/basic.py", line 1105, in _subs    rv = self._eval_subs(old, new)         ^^^^^^^^^^^^^^^^^^^^^^^^^  File "/home/oscar/current/sympy/sympy.git/sympy/core/function.py", line 1746, in _eval_subs    nfree = new.xreplace(syms).free_symbols            ^^^^^^^^^^^^^^^^^^TypeError: Basic.xreplace() missing 1 required positional argument: 'rule'Uncaught exception. Entering post mortem debuggingRunning 'cont' or 'step' will restart the program>/home/oscar/current/sympy/sympy.git/sympy/core/function.py(1746)_eval_subs()-> nfree = new.xreplace(syms).free_symbols(Pdb) p new.xreplace<function FunctionClass.xreplace.<locals>.<lambda> at 0x7f0dd0f763e0>(Pdb) p new.xreplace(syms)cos(Pdb) p new.xreplace(syms).free_symbolsset()

The actual arrangement of SymPy classes is something like this:

classManagedProperties(type):def__init__(cls,*args,**kws):passclassBasic(metaclass=ManagedProperties):defxreplace(self,rule):print('Basic')classExpr(Basic):passclassFunctionClass(ManagedProperties):@propertydefxreplace(self):returnlambdarule:print('functionclass')classApplication(Basic,metaclass=FunctionClass):passclassFunction(Application,Expr):passclasscos(Function):passcos.xreplace(1)

Your environment

  • CPython versions tested on: 3.11.0a1-3.11.0b4 (3.10.5 or lower does not have the bug)
  • Operating system and architecture: Ubuntu x86-64.

Metadata

Metadata

Assignees

Labels

3.11only security fixes3.12only security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions


    [8]ページ先頭

    ©2009-2025 Movatter.jp