This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: field "mro" behaves strangely in dataclass
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.10
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: eric.smith Nosy List: eric.araujo, eric.smith, finite-state-machine, serhiy.storchaka
Priority: normal Keywords:

Created on 2021-10-19 23:25 by finite-state-machine, last changed 2022-04-11 14:59 by admin.

Messages (9)
msg404378 - (view) Author: Finite State Machine (finite-state-machine) Date: 2021-10-19 23:25
The following Python script:

    from dataclasses import dataclass

    @dataclass
    class A:
        mro: object
        x: object

Results in the following unexpected exception:

    Traceback (most recent call last):
      File "/Users/dsuffling/.pyenv/versions/3.10.0/lib/python3.10/runpy.py", line 196, in _run_module_as_main
        return _run_code(code, main_globals, None,
      File "/Users/dsuffling/.pyenv/versions/3.10.0/lib/python3.10/runpy.py", line 86, in _run_code
        exec(code, run_globals)
      File "/Users/dsuffling/names/junk.py", line 6, in <module>
        class A:
      File "/Users/dsuffling/.pyenv/versions/3.10.0/lib/python3.10/dataclasses.py", line 1178, in dataclass
        return wrap(cls)
      File "/Users/dsuffling/.pyenv/versions/3.10.0/lib/python3.10/dataclasses.py", line 1169, in wrap
        return _process_class(cls, init, repr, eq, order, unsafe_hash,
      File "/Users/dsuffling/.pyenv/versions/3.10.0/lib/python3.10/dataclasses.py", line 1019, in _process_class
        _init_fn(all_init_fields,
      File "/Users/dsuffling/.pyenv/versions/3.10.0/lib/python3.10/dataclasses.py", line 540, in _init_fn
        raise TypeError(f'non-default argument {f.name!r} '
    TypeError: non-default argument 'x' follows default argument


The name of the first attribute ('mro') is critical; without it the problem does not occur.

It appears that 'mro' is somehow interacting with the 'mro' attribute of the 'type' object.

This issue has been verified to occur with CPython 3.10.0.
msg404381 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2021-10-19 23:49
I agree on your analysis. You'll get the same error on any name that type defines (like __class__), but "mro" looks like the only one without dunders.

I'm not sure the best way to fix this. I'll give it some thought.

Another problem is that assigning a default value breaks the .mro() call:

@dataclass
class A:
    mro: object = 3

>>> A.mro()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
msg404414 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-10-20 10:10
In Enum it is just implicitly forbidden:

>>> from enum import *
>>> class A(Enum):
...   mro = 1
...   x = 2
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/serhiy/py/cpython/Lib/enum.py", line 430, in __new__
    raise ValueError('Invalid enum member name: {0}'.format(
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: Invalid enum member name: mro
msg404430 - (view) Author: Finite State Machine (finite-state-machine) Date: 2021-10-20 11:49
For what it's worth, I think a sensible exception message solves this problem. While it would be nice to be able to use a field called 'mro', that's an enhancement; the misleading exception message is a bug.
msg404458 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2021-10-20 14:06
I think the only other thing that could be done is to have a special test for "default is type.mro", and if so, don't assume it's a default value. Which means that you could never actually use:

@dataclass
class A:
   mro: object = type.mro

But it's probably best to just disallow a field named "mro". Which is unfortunate, but such is life. It's a shame mro isn't a builtin, so we could do mro(A) instead of A.mro().
msg404819 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2021-10-22 19:57
If dataclasses wanted to allow fields named `mro`, it could replace its call to `cls.mro()` with `type.mro(cls)`.  But I don’t know if there is a strong use case for such a field.
msg404824 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-10-22 20:50
Where does dataclasses call mro()?
msg404846 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2021-10-22 23:56
The problem is that dataclasses is looking for a default value for a field by looking at getattr(cls, fieldname), which returns a value when fieldname is "mro".

I think the best thing to do, at least for now, is prohibit a field named "mro".

Ultimately I'd like to see cls.mro go away (maybe being replaced by a builtin), but I realize that's not likely to happen.
msg404857 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2021-10-23 07:20
Would not be more correct to look at cls.__dict__[fieldname]?

BTW, mro() cannot be builtin, because you should be able to override it in some classes.
History
Date User Action Args
2022-04-11 14:59:51adminsetgithub: 89694
2021-10-23 07:20:55serhiy.storchakasetmessages: + msg404857
2021-10-22 23:56:51eric.smithsetmessages: + msg404846
2021-10-22 20:50:03serhiy.storchakasetmessages: + msg404824
2021-10-22 19:57:34eric.araujosetnosy: + eric.araujo
messages: + msg404819
2021-10-20 14:06:40eric.smithsetmessages: + msg404458
2021-10-20 11:49:52finite-state-machinesetmessages: + msg404430
2021-10-20 10:10:09serhiy.storchakasetnosy: + serhiy.storchaka
messages: + msg404414
2021-10-19 23:49:38eric.smithsetmessages: + msg404381
2021-10-19 23:37:23eric.smithsetassignee: eric.smith

nosy: + eric.smith
2021-10-19 23:25:44finite-state-machinecreate