Skip to content
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

Cython mishandles decorated __new__ #3094

Open
danielcranford opened this issue Aug 23, 2019 · 2 comments

Comments

@danielcranford
Copy link

commented Aug 23, 2019

Consider the following class

class A(object):
  @my_decorator
  def __new__(cls, *args, **kwargs):
    return super(A, cls).__new__(cls, *args, **kwargs)

This class generates the following error when instantiated

TypeError: unbound method cython_function_or_method object must be called with A instance as first argument (got type instance instead)

__new__ is supposed to be special-cased to not require a @staticmethod decorator.

Additionally, attempting to work around the issue by adding @staticmethod also does not work and generates the same error.

class A(object):
  @staticmethod
  @my_decorator
  def __new__(cls, *args, **kwargs):
    return super(A, cls).__new__(cls, *args, **kwargs)

This appears to occur because when Cython compiles to C, it inverts the order of the decorator application, applying @staticmethod first. Eg, it generates C code like

  /* "module/A.py"
 *     @staticmethod
 *     @my_decorator             # <<<<<<<<<<<<<<
 *     def __new__(cls, *args, **kwargs): 
 */
  __Pyx_GetModuleGlobalName(func_my_decorator, __pyx_n_s_cache_2); if (unlikely(!func_my_decorator)) __PYX_ERR(0, 72, __pyx_L1_error)
  __Pyx_GOTREF(func_my_decorator);

  /* "module/A.py"
 *     @staticmethod
 *     @my_decorator
 *     def __new__(cls, *args, **kwargs):             # <<<<<<<<<<<<<<
 */
  func_new = __Pyx_CyFunction_NewEx(&__pyx_mdef_9module_15A_1__new__, __Pyx_CYFUNCTION_STATICMETHOD, __pyx_n_s_A___new, NULL, __pyx_n_s_module, __pyx_d, ((PyObject *)__pyx_codeobj__10)); if (unlikely(!func_new)) __PYX_ERR(0, 75, __pyx_L1_error)
  __Pyx_GOTREF(func_new);

  /* "module/A.py":71
 *     @staticmethod             # <<<<<<<<<<<<<<
 *     @cache
 *     def __new__(cls, *args, **kwargs): 
 */
  func_staticmethod_new = __Pyx_PyObject_CallOneArg(__pyx_builtin_staticmethod, func_new); if (unlikely(!func_staticmethod_new)) __PYX_ERR(0, 71, __pyx_L1_error)
  __Pyx_GOTREF(func_staticmethod_new);

One workaround is to declare the class using the semantically identical

class A(object):
  @my_decorator
  def __new__(cls, *args, **kwargs):
    return super(A, cls).__new__(cls, *args, **kwargs)
  __new__ = staticmethod(__new__)
@jdemeyer

This comment has been minimized.

Copy link
Contributor

commented Aug 27, 2019

I believe that #3102 fixes this. But I can't tell for sure since I don't know your complete use case.

@scoder

This comment has been minimized.

Copy link
Contributor

commented Aug 28, 2019

#3102 is merged, please retest this with the master branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.