Skip to content

pow(1, None, 0) raises ValueError instead of TypeError #7724

@jseop-lim

Description

@jseop-lim

Feature

Reproduction

pow(1, None, 0)

RustPython:

ValueError: pow() 3rd argument cannot be 0

CPython:

TypeError: unsupported operand type(s) for ** or pow(): 'int', 'NoneType', 'int'

Root cause

PyInt::modpow downcasts modulus to PyInt and immediately raises ValueError("pow() 3rd argument cannot be 0") when it is zero, without first validating that other is an integer. CPython's long_pow instead runs CHECK_BINOP(v, w) and the x type-check before the zero-modulus branch, so a non-integer exponent (or non-int/non-None modulus) returns NotImplemented and the dispatch falls through to a TypeError.

The same observation applies to the related variant pow(True, -3939.40..., False): once PyInt::modpow correctly returns NotImplemented for a non-int exponent, the ternary_op fallback dispatches to PyFloat::AS_NUMBER.power, which raises the expected TypeError("pow() 3rd argument not allowed unless all arguments are integers").

Fix

In PyInt::modpow, downcast other to PyInt first and return NotImplemented if the cast fails; only then perform the zero-modulus check.

Environment

  • RustPython d248a04 (Python 3.14.0)
  • CPython v3.14.3
  • OS: Debian 12

Python Documentation or reference to CPython source code

  • CPython long_pow: Objects/longobject.c#L4888-L4941 — type-check (CHECK_BINOP, PyLong_Check(x)) runs before the zero-check:

    CHECK_BINOP(v, w);
    a = (PyLongObject*)Py_NewRef(v);
    b = (PyLongObject*)Py_NewRef(w);
    if (PyLong_Check(x)) {
        c = (PyLongObject *)Py_NewRef(x);
    }
    else if (x == Py_None)
        c = NULL;
    else {
        Py_DECREF(a); Py_DECREF(b);
        Py_RETURN_NOTIMPLEMENTED;
    }
    ...
    if (c) {
        if (_PyLong_IsZero(c)) {
            PyErr_SetString(PyExc_ValueError,
                            "pow() 3rd argument cannot be 0");
            goto Error;
        }
  • Documentation: builtins.pow

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-compatA discrepancy between RustPython and CPython

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions