mirror of
				https://github.com/astral-sh/ruff.git
				synced 2025-11-03 21:24:29 +00:00 
			
		
		
		
	[ruff] Allow more field calls from attrs (RUF009) (#19021)
				
					
				
			Summary -- Closes #19014 by identifying more `field` functions from `attrs`. We already detected these when imported from `attrs` but not the `attr` module from the same package. These functions are identical to the `attrs` versions: ```pycon >>> import attrs, attr >>> attrs.field is attr.field True >>> attrs.Factory is attr.Factory True >>> ``` Test Plan -- Regression tests based on the issue
This commit is contained in:
		
							parent
							
								
									710c60f713
								
							
						
					
					
						commit
						b00f68a23c
					
				
					 3 changed files with 31 additions and 3 deletions
				
			
		| 
						 | 
					@ -125,3 +125,20 @@ class J:
 | 
				
			||||||
class K:
 | 
					class K:
 | 
				
			||||||
    f: F = F()
 | 
					    f: F = F()
 | 
				
			||||||
    g: G = G()
 | 
					    g: G = G()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Regression test for https://github.com/astral-sh/ruff/issues/19014
 | 
				
			||||||
 | 
					# These are all valid field calls and should not cause diagnostics.
 | 
				
			||||||
 | 
					@attr.define
 | 
				
			||||||
 | 
					class TestAttrField:
 | 
				
			||||||
 | 
					    attr_field_factory: list[int] = attr.field(factory=list)
 | 
				
			||||||
 | 
					    attr_field_default: list[int] = attr.field(default=attr.Factory(list))
 | 
				
			||||||
 | 
					    attr_factory: list[int] = attr.Factory(list)
 | 
				
			||||||
 | 
					    attr_ib: list[int] = attr.ib(factory=list)
 | 
				
			||||||
 | 
					    attr_attr: list[int] = attr.attr(factory=list)
 | 
				
			||||||
 | 
					    attr_attrib: list[int] = attr.attrib(factory=list)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@attr.attributes
 | 
				
			||||||
 | 
					class TestAttrAttributes:
 | 
				
			||||||
 | 
					    x: list[int] = list()  # RUF009
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,14 +25,16 @@ fn is_stdlib_dataclass_field(func: &Expr, semantic: &SemanticModel) -> bool {
 | 
				
			||||||
        .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["dataclasses", "field"]))
 | 
					        .is_some_and(|qualified_name| matches!(qualified_name.segments(), ["dataclasses", "field"]))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Returns `true` if the given [`Expr`] is a call to `attr.ib()` or `attrs.field()`.
 | 
					/// Returns `true` if the given [`Expr`] is a call to an `attrs` field function.
 | 
				
			||||||
fn is_attrs_field(func: &Expr, semantic: &SemanticModel) -> bool {
 | 
					fn is_attrs_field(func: &Expr, semantic: &SemanticModel) -> bool {
 | 
				
			||||||
    semantic
 | 
					    semantic
 | 
				
			||||||
        .resolve_qualified_name(func)
 | 
					        .resolve_qualified_name(func)
 | 
				
			||||||
        .is_some_and(|qualified_name| {
 | 
					        .is_some_and(|qualified_name| {
 | 
				
			||||||
            matches!(
 | 
					            matches!(
 | 
				
			||||||
                qualified_name.segments(),
 | 
					                qualified_name.segments(),
 | 
				
			||||||
                ["attrs", "field" | "Factory"] | ["attr", "ib"]
 | 
					                ["attrs", "field" | "Factory"]
 | 
				
			||||||
 | 
					                // See https://github.com/python-attrs/attrs/blob/main/src/attr/__init__.py#L33
 | 
				
			||||||
 | 
					                    | ["attr", "ib" | "attr" | "attrib" | "field" | "Factory"]
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -120,7 +122,8 @@ pub(super) fn dataclass_kind<'a>(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match qualified_name.segments() {
 | 
					        match qualified_name.segments() {
 | 
				
			||||||
            ["attrs" | "attr", func @ ("define" | "frozen" | "mutable")]
 | 
					            ["attrs" | "attr", func @ ("define" | "frozen" | "mutable")]
 | 
				
			||||||
            | ["attr", func @ ("s" | "attrs")] => {
 | 
					            // See https://github.com/python-attrs/attrs/blob/main/src/attr/__init__.py#L32
 | 
				
			||||||
 | 
					            | ["attr", func @ ("s" | "attributes" | "attrs")] => {
 | 
				
			||||||
                // `.define`, `.frozen` and `.mutable` all default `auto_attribs` to `None`,
 | 
					                // `.define`, `.frozen` and `.mutable` all default `auto_attribs` to `None`,
 | 
				
			||||||
                // whereas `@attr.s` implicitly sets `auto_attribs=False`.
 | 
					                // whereas `@attr.s` implicitly sets `auto_attribs=False`.
 | 
				
			||||||
                // https://www.attrs.org/en/stable/api.html#attrs.define
 | 
					                // https://www.attrs.org/en/stable/api.html#attrs.define
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,3 +98,11 @@ RUF009_attrs.py:127:12: RUF009 Do not perform function call `G` in dataclass def
 | 
				
			||||||
127 |     g: G = G()
 | 
					127 |     g: G = G()
 | 
				
			||||||
    |            ^^^ RUF009
 | 
					    |            ^^^ RUF009
 | 
				
			||||||
    |
 | 
					    |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUF009_attrs.py:144:20: RUF009 Do not perform function call `list` in dataclass defaults
 | 
				
			||||||
 | 
					    |
 | 
				
			||||||
 | 
					142 | @attr.attributes
 | 
				
			||||||
 | 
					143 | class TestAttrAttributes:
 | 
				
			||||||
 | 
					144 |     x: list[int] = list()  # RUF009
 | 
				
			||||||
 | 
					    |                    ^^^^^^ RUF009
 | 
				
			||||||
 | 
					    |
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue