mkdocstrings / griffe

Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API.
https://mkdocstrings.github.io/griffe
ISC License
261 stars 38 forks source link

bug: parameter kinds for dataclasses should match the non-dataclass equivalents #234

Closed has2k1 closed 2 months ago

has2k1 commented 3 months ago

Currently, parameters derived from dataclass fields have no kind information.

This is mainly for python 3.10+ which have keyword_only specification for dataclass fields. Here are failing test cases.

from griffe.tests import temporary_visited_module

code = """
from dataclasses import dataclass, field, KW_ONLY

# Pair of dataclass and equivalent non-dataclass
# The parameter kinds for each pair should be the same

@dataclass
class PointA:
    x: float
    y: float = field(kw_only=True)

class PointA_:
    def __init__(self, x: float, *, y: float): ...

@dataclass
class PointB:
    x: float = field(kw_only=True)
    y: float

class PointB_:
    def __init__(self, y: float, *, x: float): ...

@dataclass
class PointC:
    x: float
    _: KW_ONLY
    y: float

class PointC_:
    def __init__(self, x: float, *, y: float): ...

@dataclass
class PointD:
    _: KW_ONLY
    x: float
    y: float

class PointD_:
    def __init__(self, *, x: float, y: float): ...

@dataclass(kw_only=True)
class PointE:
    x: float
    y: float

class PointE_:
    def __init__(self, *, x: float, y: float): ...
"""

def parameter_kinds(obj):
    return [(p.kind and p.kind.value) or None for p in obj.parameters]

with temporary_visited_module(code) as module:
    def both_parameter_kinds(s):
        return [parameter_kinds(obj) for obj in (module[s], module[f"{s}_"])]

    kindsA, kindsA_ = both_parameter_kinds("PointA")
    kindsB, kindsB_ = both_parameter_kinds("PointB")
    kindsC, kindsC_ = both_parameter_kinds("PointC")
    kindsD, kindsD_ = both_parameter_kinds("PointD")
    kindsE, kindsE_ = both_parameter_kinds("PointE")

    assert kindsA == kindsA_
    assert kindsB == kindsB_
    assert kindsC == kindsC_
    assert kindsD == kindsD_
    assert kindsE == kindsE_