mirror of
				https://github.com/astral-sh/ruff.git
				synced 2025-11-03 21:24:29 +00:00 
			
		
		
		
	[ty] dataclass_transform: Support frozen_default and kw_only_default (#20761)
	
		
			
	
		
	
	
		
	
		
			Some checks are pending
		
		
	
	
		
			
				
	
				CI / Determine changes (push) Waiting to run
				
			
		
			
				
	
				CI / cargo fmt (push) Waiting to run
				
			
		
			
				
	
				CI / cargo clippy (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo test (linux) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo test (linux, release) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo test (windows) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo test (wasm) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo build (release) (push) Waiting to run
				
			
		
			
				
	
				CI / cargo build (msrv) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo fuzz build (push) Blocked by required conditions
				
			
		
			
				
	
				CI / fuzz parser (push) Blocked by required conditions
				
			
		
			
				
	
				CI / test scripts (push) Blocked by required conditions
				
			
		
			
				
	
				CI / ecosystem (push) Blocked by required conditions
				
			
		
			
				
	
				CI / Fuzz for new ty panics (push) Blocked by required conditions
				
			
		
			
				
	
				CI / cargo shear (push) Blocked by required conditions
				
			
		
			
				
	
				CI / ty completion evaluation (push) Blocked by required conditions
				
			
		
			
				
	
				CI / python package (push) Waiting to run
				
			
		
			
				
	
				CI / pre-commit (push) Waiting to run
				
			
		
			
				
	
				CI / mkdocs (push) Waiting to run
				
			
		
			
				
	
				CI / formatter instabilities and black similarity (push) Blocked by required conditions
				
			
		
			
				
	
				CI / test ruff-lsp (push) Blocked by required conditions
				
			
		
			
				
	
				CI / check playground (push) Blocked by required conditions
				
			
		
			
				
	
				CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / benchmarks instrumented (ty) (push) Blocked by required conditions
				
			
		
			
				
	
				CI / benchmarks-walltime (push) Blocked by required conditions
				
			
		
			
				
	
				[ty Playground] Release / publish (push) Waiting to run
				
			
		
		
	
	
				
					
				
			
		
			Some checks are pending
		
		
	
	CI / Determine changes (push) Waiting to run
				
			CI / cargo fmt (push) Waiting to run
				
			CI / cargo clippy (push) Blocked by required conditions
				
			CI / cargo test (linux) (push) Blocked by required conditions
				
			CI / cargo test (linux, release) (push) Blocked by required conditions
				
			CI / cargo test (windows) (push) Blocked by required conditions
				
			CI / cargo test (wasm) (push) Blocked by required conditions
				
			CI / cargo build (release) (push) Waiting to run
				
			CI / cargo build (msrv) (push) Blocked by required conditions
				
			CI / cargo fuzz build (push) Blocked by required conditions
				
			CI / fuzz parser (push) Blocked by required conditions
				
			CI / test scripts (push) Blocked by required conditions
				
			CI / ecosystem (push) Blocked by required conditions
				
			CI / Fuzz for new ty panics (push) Blocked by required conditions
				
			CI / cargo shear (push) Blocked by required conditions
				
			CI / ty completion evaluation (push) Blocked by required conditions
				
			CI / python package (push) Waiting to run
				
			CI / pre-commit (push) Waiting to run
				
			CI / mkdocs (push) Waiting to run
				
			CI / formatter instabilities and black similarity (push) Blocked by required conditions
				
			CI / test ruff-lsp (push) Blocked by required conditions
				
			CI / check playground (push) Blocked by required conditions
				
			CI / benchmarks instrumented (ruff) (push) Blocked by required conditions
				
			CI / benchmarks instrumented (ty) (push) Blocked by required conditions
				
			CI / benchmarks-walltime (push) Blocked by required conditions
				
			[ty Playground] Release / publish (push) Waiting to run
				
			## Summary - Add support for eq, kw_only, and frozen parameter overrides in @dataclass_transform - Previously only order parameter override was supported - Update test documentation to reflect fixed behavior - Resolves issue where kw_only_default and frozen_default could not be overridden closes https://github.com/astral-sh/ty/issues/1260 ## Test Plan New Markdown tests --------- Co-authored-by: David Peter <mail@david-peter.de>
This commit is contained in:
		
							parent
							
								
									b0c6217e0b
								
							
						
					
					
						commit
						f0d0b57900
					
				
					 2 changed files with 79 additions and 23 deletions
				
			
		| 
						 | 
					@ -202,7 +202,7 @@ from typing import dataclass_transform
 | 
				
			||||||
from dataclasses import field
 | 
					from dataclasses import field
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@dataclass_transform(kw_only_default=True)
 | 
					@dataclass_transform(kw_only_default=True)
 | 
				
			||||||
def create_model(*, init=True): ...
 | 
					def create_model(*, kw_only: bool = True): ...
 | 
				
			||||||
@create_model()
 | 
					@create_model()
 | 
				
			||||||
class A:
 | 
					class A:
 | 
				
			||||||
    name: str
 | 
					    name: str
 | 
				
			||||||
| 
						 | 
					@ -213,24 +213,69 @@ a = A(name="Harry")
 | 
				
			||||||
a = A("Harry")
 | 
					a = A("Harry")
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TODO: This can be overridden by the call to the decorator function.
 | 
					This can be overridden by setting `kw_only=False` when applying the decorator:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```py
 | 
					```py
 | 
				
			||||||
from typing import dataclass_transform
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@dataclass_transform(kw_only_default=True)
 | 
					 | 
				
			||||||
def create_model(*, kw_only: bool = True): ...
 | 
					 | 
				
			||||||
@create_model(kw_only=False)
 | 
					@create_model(kw_only=False)
 | 
				
			||||||
class CustomerModel:
 | 
					class CustomerModel:
 | 
				
			||||||
    id: int
 | 
					    id: int
 | 
				
			||||||
    name: str
 | 
					    name: str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# TODO: Should not emit errors
 | 
					 | 
				
			||||||
# error: [missing-argument]
 | 
					 | 
				
			||||||
# error: [too-many-positional-arguments]
 | 
					 | 
				
			||||||
c = CustomerModel(1, "Harry")
 | 
					c = CustomerModel(1, "Harry")
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `frozen_default`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When provided, sets the default value for the `frozen` parameter of `field()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```py
 | 
				
			||||||
 | 
					from typing import dataclass_transform
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@dataclass_transform(frozen_default=True)
 | 
				
			||||||
 | 
					def create_model(*, frozen: bool = True): ...
 | 
				
			||||||
 | 
					@create_model()
 | 
				
			||||||
 | 
					class ImmutableModel:
 | 
				
			||||||
 | 
					    name: str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					i = ImmutableModel(name="test")
 | 
				
			||||||
 | 
					i.name = "new"  # error: [invalid-assignment]
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Again, this can be overridden by setting `frozen=False` when applying the decorator:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```py
 | 
				
			||||||
 | 
					@create_model(frozen=False)
 | 
				
			||||||
 | 
					class MutableModel:
 | 
				
			||||||
 | 
					    name: str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					m = MutableModel(name="test")
 | 
				
			||||||
 | 
					m.name = "new"  # No error
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Combining parameters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Combining several of these parameters also works as expected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```py
 | 
				
			||||||
 | 
					from typing import dataclass_transform
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@dataclass_transform(eq_default=True, order_default=False, kw_only_default=True, frozen_default=True)
 | 
				
			||||||
 | 
					def create_model(*, eq: bool = True, order: bool = False, kw_only: bool = True, frozen: bool = True): ...
 | 
				
			||||||
 | 
					@create_model(eq=False, order=True, kw_only=False, frozen=False)
 | 
				
			||||||
 | 
					class OverridesAllParametersModel:
 | 
				
			||||||
 | 
					    name: str
 | 
				
			||||||
 | 
					    age: int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Positional arguments are allowed:
 | 
				
			||||||
 | 
					model = OverridesAllParametersModel("test", 25)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Mutation is allowed:
 | 
				
			||||||
 | 
					model.name = "new"  # No error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Comparison methods are generated:
 | 
				
			||||||
 | 
					model < model  # No error
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### `field_specifiers`
 | 
					### `field_specifiers`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To do
 | 
					To do
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1023,18 +1023,30 @@ impl<'db> Bindings<'db> {
 | 
				
			||||||
                                            let mut dataclass_params =
 | 
					                                            let mut dataclass_params =
 | 
				
			||||||
                                                DataclassParams::from(params);
 | 
					                                                DataclassParams::from(params);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                            if let Some(Some(Type::BooleanLiteral(order))) =
 | 
					                                            if let Ok(Some(Type::BooleanLiteral(order))) =
 | 
				
			||||||
                                                overload
 | 
					                                                overload.parameter_type_by_name("order")
 | 
				
			||||||
                                                    .signature
 | 
					                                            {
 | 
				
			||||||
                                                    .parameters()
 | 
					                                                dataclass_params.set(DataclassParams::ORDER, order);
 | 
				
			||||||
                                                    .keyword_by_name("order")
 | 
					                                            }
 | 
				
			||||||
                                                    .map(|(idx, _)| idx)
 | 
					
 | 
				
			||||||
                                                    .and_then(|idx| {
 | 
					                                            if let Ok(Some(Type::BooleanLiteral(eq))) =
 | 
				
			||||||
                                                        overload.parameter_types().get(idx)
 | 
					                                                overload.parameter_type_by_name("eq")
 | 
				
			||||||
                                                    })
 | 
					                                            {
 | 
				
			||||||
 | 
					                                                dataclass_params.set(DataclassParams::EQ, eq);
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                            if let Ok(Some(Type::BooleanLiteral(kw_only))) =
 | 
				
			||||||
 | 
					                                                overload.parameter_type_by_name("kw_only")
 | 
				
			||||||
                                            {
 | 
					                                            {
 | 
				
			||||||
                                                dataclass_params
 | 
					                                                dataclass_params
 | 
				
			||||||
                                                    .set(DataclassParams::ORDER, *order);
 | 
					                                                    .set(DataclassParams::KW_ONLY, kw_only);
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                            if let Ok(Some(Type::BooleanLiteral(frozen))) =
 | 
				
			||||||
 | 
					                                                overload.parameter_type_by_name("frozen")
 | 
				
			||||||
 | 
					                                            {
 | 
				
			||||||
 | 
					                                                dataclass_params
 | 
				
			||||||
 | 
					                                                    .set(DataclassParams::FROZEN, frozen);
 | 
				
			||||||
                                            }
 | 
					                                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                            Type::DataclassDecorator(dataclass_params)
 | 
					                                            Type::DataclassDecorator(dataclass_params)
 | 
				
			||||||
| 
						 | 
					@ -2933,12 +2945,11 @@ impl<'db> Binding<'db> {
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        parameter_name: &str,
 | 
					        parameter_name: &str,
 | 
				
			||||||
    ) -> Result<Option<Type<'db>>, UnknownParameterNameError> {
 | 
					    ) -> Result<Option<Type<'db>>, UnknownParameterNameError> {
 | 
				
			||||||
        let (index, _) = self
 | 
					        let index = self
 | 
				
			||||||
            .signature
 | 
					            .signature
 | 
				
			||||||
            .parameters()
 | 
					            .parameters()
 | 
				
			||||||
            .iter()
 | 
					            .keyword_by_name(parameter_name)
 | 
				
			||||||
            .enumerate()
 | 
					            .map(|(i, _)| i)
 | 
				
			||||||
            .find(|(_, param)| param.name().is_some_and(|name| name == parameter_name))
 | 
					 | 
				
			||||||
            .ok_or(UnknownParameterNameError)?;
 | 
					            .ok_or(UnknownParameterNameError)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(self.parameter_tys[index])
 | 
					        Ok(self.parameter_tys[index])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue