Skip to content

N815 (mixed-case-variable-in-class-scope) probably should not check TypedDict #4014

@sirrus233

Description

@sirrus233

Hey, thanks for writing ruff!! The following example illustrates two violations of N815, on the mixed-case variable myOtherVar. The first violation is of course appropriate. However, I think the second is misleading, and this rule should not apply to TypedDict.

from typing import TypedDict

class Foo:
    my_var: int
    myOtherVar: str  # Violation! Makes sense.

class Bar(TypedDict):
    my_var: int
    myOtherVar: str  # Violation! But shouldn't be.

A TypedDict is less of a real class, and more syntactic sugar to add typing to the keys of a dictionary. The argument here would be that if {"my_var": 0, "myOtherVar": "xyz"} would be appropriate, then the TypedDict form should be too. It may also be worth noting that the equivalent example:

Bar = TypedDict("Bar", {"my_var": int, "myOtherVar": str})

does not flag this check (although it does flag UP013, as it should).

There is a practical reason to relax the rule here: a TypedDict is a useful and lightweight way to deal with adding static typing to an untyped blob of JSON with known structure sent over the wire (say, the body of an HTTP request). The writer of the TypedDict may not have control over the structure of the untyped dict, so it becomes awkward to comply with N815 without:

  • Writing an awkward camel_to_snake parser
  • Using a functional TypedDict definition (and falling afoul of UP013)
  • Dropping a noqa on every camelCased field
  • Disabling N815
  • Using a heavier ser/de library

All of which are undesirable.

I want to point out that the current behavior of N815 does conform to the behavior of the rule upstream in pep8-naming. I'm not sure about Ruff's overall stance on modifying how rules behave, so if enforcing exact parity is important, then there probably isn't anything to be done here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionAsking for support or clarification

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions