Skip to content

reprlib (used by pytest) infers builtin types based on class __name__ #113570

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

Closed
jpivarski opened this issue Dec 29, 2023 · 1 comment
Closed
Labels
3.12 only security fixes 3.13 bugs and security fixes 3.14 bugs and security fixes type-bug An unexpected behavior, bug, or error

Comments

@jpivarski
Copy link

jpivarski commented Dec 29, 2023

Bug report

Bug description:

Code like the following:

import reprlib

class array:
    def __repr__(self):
        return "not array.array!"

reprlib.repr(array())

raises

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jpivarski/mambaforge/lib/python3.10/reprlib.py", line 52, in repr
    return self.repr1(x, self.maxlevel)
  File "/home/jpivarski/mambaforge/lib/python3.10/reprlib.py", line 60, in repr1
    return getattr(self, 'repr_' + typename)(x, level)
  File "/home/jpivarski/mambaforge/lib/python3.10/reprlib.py", line 86, in repr_array
    header = "array('%s', [" % x.typecode
AttributeError: 'array' object has no attribute 'typecode'

because reprlib uses type(x).__name__ to infer that x has type array.array, rather than my user-defined class:

cpython/Lib/reprlib.py

Lines 62 to 70 in cf34b77

def repr1(self, x, level):
typename = type(x).__name__
if ' ' in typename:
parts = typename.split()
typename = '_'.join(parts)
if hasattr(self, 'repr_' + typename):
return getattr(self, 'repr_' + typename)(x, level)
else:
return self.repr_instance(x, level)

Perhaps there's good reason to check the __name__ string instead of isinstance(x, array.array), to avoid unnecessarily importing the array module for start-up time or for minimizing clutter in the sys.modules. However, this test should check both the __name__ string and the __module__ string.

This affects any user-defined classes with the following names:

  • tuple
  • list
  • array
  • set
  • frozenset
  • deque
  • dict
  • str
  • int
  • instance

Some of these, admittedly, would be bad names for user-defined classes, but others are more reasonable, such as array and deque.1

Since these methods are publicly available on class reprlib.Repr, the method names can't change, but the lookup could be replaced using a dict like

class Repr:
    _lookup = {
        ("builtins", "tuple"): repr_tuple,
        ("builtins", "list"): repr_list,
        ("array", "array"): repr_array,
        ("builtins", "set"): repr_set,
        ("builtins", "frozenset"): repr_frozenset,
        ("collections", "deque"): repr_deque,
        ("builtins", "dict"): repr_dict,
        ("builtins", "str"): repr_str,
        ("builtins", "int"): repr_int,
    }

I encountered this in pytest. My error output contained

x          = <[ValueError('the truth value of an array whose length is not 1 is ambiguous; use ak.any() or ak.all()') raised in repr()] array object at 0x779b54763700>
y          = array([1, 2, 3])

or

x          = <[AttributeError("'array' object has no attribute 'typecode'") raised in repr()] array object at 0x728278bacd60>
y          = array([1, 2, 3])

for reasons that had nothing to do with the actual error, and the array.__repr__ code itself is error-free. (pytest overrides reprlib to provide a SafeRepr.)

Thanks!

CPython versions tested on:

3.10

Operating systems tested on:

Linux

Linked PRs

Footnotes

  1. In my case, I want the ragged library to provide a ragged.array because it reads like English that way. I also don't want to change the __name__ of my class to differ from its actual name. In particular, the Array API specification uses "array" as a type name.

@jpivarski jpivarski added the type-bug An unexpected behavior, bug, or error label Dec 29, 2023
@jpivarski
Copy link
Author

This is what I'm using as a workaround for pytest, in my conftest.py file:

import reprlib
import pytest
import my_own_library

@pytest.fixture(scope="session", autouse=True)
def _patch_reprlib():
    if not hasattr(reprlib.Repr, "repr1_original"):

        def repr1(self, x, level):
            if isinstance(x, ragged.array):
                return self.repr_instance(x, level)
            return self.repr1_original(x, level)

        reprlib.Repr.repr1_original = reprlib.Repr.repr1
        reprlib.Repr.repr1 = repr1

georgepittock added a commit to georgepittock/cpython that referenced this issue Jan 3, 2024
georgepittock added a commit to georgepittock/cpython that referenced this issue Jan 3, 2024
georgepittock added a commit to georgepittock/cpython that referenced this issue Oct 17, 2024
@serhiy-storchaka serhiy-storchaka added 3.12 only security fixes 3.13 bugs and security fixes 3.14 bugs and security fixes labels Oct 17, 2024
georgepittock added a commit to georgepittock/cpython that referenced this issue Oct 17, 2024
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Oct 17, 2024
…dowed builtins (pythonGH-113577)

(cherry picked from commit 04d6dd2)

Co-authored-by: George Pittock <[email protected]>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Oct 17, 2024
…dowed builtins (pythonGH-113577)

(cherry picked from commit 04d6dd2)

Co-authored-by: George Pittock <[email protected]>
serhiy-storchaka pushed a commit that referenced this issue Oct 17, 2024
…adowed builtins (GH-113577) (GH-125655)

(cherry picked from commit 04d6dd2)

Co-authored-by: George Pittock <[email protected]>
serhiy-storchaka pushed a commit that referenced this issue Oct 17, 2024
…adowed builtins (GH-113577) (GH-125654)

(cherry picked from commit 04d6dd2)

Co-authored-by: George Pittock <[email protected]>
ebonnal pushed a commit to ebonnal/cpython that referenced this issue Jan 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.12 only security fixes 3.13 bugs and security fixes 3.14 bugs and security fixes type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

2 participants