Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,48 @@ class InvalidKWOnlyDefaultModel:
z: bytes = field(kw_only=False) # error: [dataclass-field-order]
```

### Keyword-only field specifiers before Python 3.10

Although `dataclasses.field` does not support `kw_only` before Python 3.10, third-party field
specifiers can support it on earlier Python versions. Inherited keyword-only fields must retain that
setting so they do not participate in positional field ordering.

```toml
[environment]
python-version = "3.9"
```

```py
from typing import Any, TypeVar
from typing_extensions import dataclass_transform

T = TypeVar("T")

def custom_field(*, default: Any = ..., kw_only: bool = False) -> Any: ...
@dataclass_transform(field_specifiers=(custom_field,))
def custom_dataclass(cls: type[T]) -> type[T]:
return cls

@custom_dataclass
class Base:
optional: float = custom_field(default=1.0, kw_only=True)

@custom_dataclass
class Child(Base):
required: str

reveal_type(Child.__init__) # revealed: (self: Child, required: str, *, optional: float = ...) -> None

Child("value")
Child("value", optional=2.0)
Child("value", 2.0) # error: [too-many-positional-arguments]

@custom_dataclass
class InvalidChild(Base):
positional_default: str = custom_field(default="default", kw_only=False)
required: int # error: [dataclass-field-order]
```

### For metaclass-based transformers

```py
Expand Down
6 changes: 5 additions & 1 deletion crates/ty_python_semantic/src/types/call/bind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1986,7 +1986,11 @@ impl<'db> Bindings<'db> {
.map(|init| !init.bool(db, env).is_always_false())
.unwrap_or(true);

let kw_only = if env.python_version(db) >= PythonVersion::PY310 {
// Only the standard-library field specifier requires Python 3.10 for
// `kw_only`; third-party field specifiers can support it earlier.
let kw_only = if env.python_version(db) >= PythonVersion::PY310
|| !function_type.is_known(db, KnownFunction::Field)
{
match kw_only.and_then(Type::as_literal_value_kind) {
// We are more conservative here when turning the type for `kw_only`
// into a bool, because a field specifier in a stub might use
Expand Down
Loading