Skip to content

Strange "Attributes without a default cannot follow attributes with one" when extending a dataclass from a package #6960

Closed
@reinhrst

Description

@reinhrst

Apologies upfront, this must be the worst bug report I've ever made. I can only reproduce it every now and then, and only in a quite complicated code base. Just logging it here in case it rings a bell (or someone else has the same issue).

I have a package (which we distribute through our own, private, pypi), with type-support (py.typed file present). There are no stub files, all types are inline.

The package defines a dataclass, e.g. file cars.py

 1. import dataclasses
 2.
 3.
 4.
 5. # some empty lines to illustrate a point
 6.
 7.
 8.
 9.
10. @dataclasses.dataclass(frozen=True)
11. class Car:
12.    name: str
13.    top_speed: float
14.    brand: str = "Renault"

Now in my main program (main.py) I need to get a dataclass that is very much like a Car, I just need to be able to set the top_speed to "uknown" (a string).

 1. import dataclasses
 2. from mypackage import cars
 3.
 4. @dataclasses.dataclass(frozen=True)
 5. class UnknownCar(cars.Car):
 6.    top_speed: str
 7.
 8. mycar = UnknownCar(name="unknown", top_speed="unkown", brand="unknown")

Now I would expect mypy to give a warning (Subclass not compatible with super class). What happens however is this:

The first run, no errors. Subsequent runs (same command) give the errors below (until .mypy-cache/ is removed, after which the first run again is errorfree)

main.py:6 error: Attributes without a default cannot follow attributes with one
main.py:12 error: Attributes without a default cannot follow attributes with one
main.py:13 error: Attributes without a default cannot follow attributes with one

So what seems to happen is that mypy (incorrectly) assumes that the top_speed for UnknownCar will be added as last argument (and then it complains, correctly, that the argument before it, brand, has a default)

The next 2 error however point to the lines where name and top_speed are defined in cars.py (even though the error says main.py). If I add more newlines to the cars.py file, these line numbers in the errormessage change, and if I give default values to them, the errors disappear. By giving a fake default (of dataclasses.field()) to all entries, all errors disappear. Note that no amount of type: ignore in the main.py file seems to be able to get rid of the errors.

The behaviour was observed (however not always consistently) in mypy 0.670, 0.701 and HEAD.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions