diff --git a/crates/ty/docs/rules.md b/crates/ty/docs/rules.md
index 78f757a6bb..5ac36c4fb9 100644
--- a/crates/ty/docs/rules.md
+++ b/crates/ty/docs/rules.md
@@ -357,7 +357,7 @@ def test(): -> "Literal[5]":
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -387,7 +387,7 @@ class C(A, B): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -502,7 +502,7 @@ an atypical memory layout.
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -529,7 +529,7 @@ func("foo") # error: [invalid-argument-type]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -557,7 +557,7 @@ a: int = ''
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -591,7 +591,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
Default level: error ·
Added in 0.0.1-alpha.19 ·
Related issues ·
-View source
+View source
@@ -627,7 +627,7 @@ asyncio.run(main())
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -651,7 +651,7 @@ class A(42): ... # error: [invalid-base]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -678,7 +678,7 @@ with 1:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -707,7 +707,7 @@ a: str
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -751,7 +751,7 @@ except ZeroDivisionError:
Default level: error ·
Added in 0.0.1-alpha.28 ·
Related issues ·
-View source
+View source
@@ -793,7 +793,7 @@ class D(A):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -826,7 +826,7 @@ class C[U](Generic[T]): ...
Default level: error ·
Added in 0.0.1-alpha.17 ·
Related issues ·
-View source
+View source
@@ -865,7 +865,7 @@ carol = Person(name="Carol", age=25) # typo!
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -900,7 +900,7 @@ def f(t: TypeVar("U")): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -934,7 +934,7 @@ class B(metaclass=f): ...
Default level: error ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -1052,7 +1052,8 @@ Checks for invalidly defined `NamedTuple` classes.
**Why is this bad?**
An invalidly defined `NamedTuple` class may lead to the type checker
-drawing incorrect conclusions. It may also lead to `TypeError`s at runtime.
+drawing incorrect conclusions. It may also lead to `TypeError`s or
+`AttributeError`s at runtime.
**Examples**
@@ -1067,13 +1068,34 @@ in a class's bases list.
TypeError: can only inherit from a NamedTuple type and Generic
```
+Further, `NamedTuple` field names cannot start with an underscore:
+
+```pycon
+>>> from typing import NamedTuple
+>>> class Foo(NamedTuple):
+... _bar: int
+ValueError: Field names cannot start with an underscore: '_bar'
+```
+
+`NamedTuple` classes also have certain synthesized attributes (like `_asdict`, `_make`,
+`_replace`, etc.) that cannot be overwritten. Attempting to assign to these attributes
+without a type annotation will raise an `AttributeError` at runtime.
+
+```pycon
+>>> from typing import NamedTuple
+>>> class Foo(NamedTuple):
+... x: int
+... _asdict = 42
+AttributeError: Cannot overwrite NamedTuple attribute _asdict
+```
+
## `invalid-newtype`
Default level: error ·
Preview (since 1.0.0) ·
Related issues ·
-View source
+View source
@@ -1103,7 +1125,7 @@ Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType`
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1153,7 +1175,7 @@ def foo(x: int) -> int: ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1179,7 +1201,7 @@ def f(a: int = ''): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1244,7 +1266,7 @@ TypeError: Protocols can only inherit from other protocols, got
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1293,7 +1315,7 @@ def g():
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1318,7 +1340,7 @@ def func() -> int:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1376,7 +1398,7 @@ TODO #14889
Default level: error ·
Added in 0.0.1-alpha.6 ·
Related issues ·
-View source
+View source
@@ -1403,7 +1425,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -1450,7 +1472,7 @@ Bar[int] # error: too few arguments
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1480,7 +1502,7 @@ TYPE_CHECKING = ''
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1510,7 +1532,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
Default level: error ·
Added in 0.0.1-alpha.11 ·
Related issues ·
-View source
+View source
@@ -1544,7 +1566,7 @@ f(10) # Error
Default level: error ·
Added in 0.0.1-alpha.11 ·
Related issues ·
-View source
+View source
@@ -1578,7 +1600,7 @@ class C:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1613,7 +1635,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1638,7 +1660,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
Default level: error ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -1671,7 +1693,7 @@ alice["age"] # KeyError
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1700,7 +1722,7 @@ func("string") # error: [no-matching-overload]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1724,7 +1746,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1750,7 +1772,7 @@ for i in 34: # TypeError: 'int' object is not iterable
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -1783,7 +1805,7 @@ class B(A):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1810,7 +1832,7 @@ f(1, x=2) # Error raised here
Default level: error ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -1868,7 +1890,7 @@ def test(): -> "int":
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1898,7 +1920,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1927,7 +1949,7 @@ class B(A): ... # Error raised here
Default level: error ·
Preview (since 0.0.1-alpha.30) ·
Related issues ·
-View source
+View source
@@ -1961,7 +1983,7 @@ class F(NamedTuple):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1988,7 +2010,7 @@ f("foo") # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2016,7 +2038,7 @@ def _(x: int):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2062,7 +2084,7 @@ class A:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2089,7 +2111,7 @@ f(x=1, y=2) # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2117,7 +2139,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2142,7 +2164,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2167,7 +2189,7 @@ print(x) # NameError: name 'x' is not defined
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2204,7 +2226,7 @@ b1 < b2 < b1 # exception raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2232,7 +2254,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2386,7 +2408,7 @@ a = 20 / 0 # type: ignore
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2446,7 +2468,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2478,7 +2500,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2505,7 +2527,7 @@ cast(int, f()) # Redundant
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2529,7 +2551,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined
Default level: warn ·
Added in 0.0.1-alpha.15 ·
Related issues ·
-View source
+View source
@@ -2587,7 +2609,7 @@ def g():
Default level: warn ·
Added in 0.0.1-alpha.7 ·
Related issues ·
-View source
+View source
@@ -2626,7 +2648,7 @@ class D(C): ... # error: [unsupported-base]
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2713,7 +2735,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime.
Default level: ignore ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
diff --git a/crates/ty_python_semantic/resources/mdtest/named_tuple.md b/crates/ty_python_semantic/resources/mdtest/named_tuple.md
index a34e115117..3d1a0ec544 100644
--- a/crates/ty_python_semantic/resources/mdtest/named_tuple.md
+++ b/crates/ty_python_semantic/resources/mdtest/named_tuple.md
@@ -500,3 +500,113 @@ class Bar(NamedTuple):
class Baz(Bar):
_whatever: str # `Baz` is not a NamedTuple class, so this is fine
```
+
+## Prohibited NamedTuple attributes
+
+`NamedTuple` classes have certain synthesized attributes that cannot be overwritten. Attempting to
+assign to these attributes (without type annotations) will raise an `AttributeError` at runtime.
+
+```py
+from typing import NamedTuple
+
+class F(NamedTuple):
+ x: int
+
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `_asdict`"
+ _asdict = 42
+
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `_make`"
+ _make = "foo"
+
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `_replace`"
+ _replace = lambda self: self
+
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `_fields`"
+ _fields = ()
+
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `_field_defaults`"
+ _field_defaults = {}
+
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `__new__`"
+ __new__ = None
+
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `__init__`"
+ __init__ = None
+
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `__getnewargs__`"
+ __getnewargs__ = None
+```
+
+However, other attributes (including those starting with underscores) can be assigned without error:
+
+```py
+from typing import NamedTuple
+
+class G(NamedTuple):
+ x: int
+
+ # These are fine (not prohibited attributes)
+ _custom = 42
+ __custom__ = "ok"
+ regular_attr = "value"
+```
+
+Note that type-annotated attributes become NamedTuple fields, not attribute overrides. They are not
+flagged as prohibited attribute overrides (though field names starting with `_` are caught by the
+underscore field name check):
+
+```py
+from typing import NamedTuple
+
+class H(NamedTuple):
+ x: int
+ # This is a field declaration, not an override. It's not flagged as an override,
+ # but is flagged because field names cannot start with underscores.
+ # error: [invalid-named-tuple] "NamedTuple field `_asdict` cannot start with an underscore"
+ _asdict: int = 0
+```
+
+The check also applies to assignments within conditional blocks:
+
+```py
+from typing import NamedTuple
+
+class I(NamedTuple):
+ x: int
+
+ if True:
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `_asdict`"
+ _asdict = 42
+```
+
+Method definitions with prohibited names are also flagged:
+
+```py
+from typing import NamedTuple
+
+class J(NamedTuple):
+ x: int
+
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `_asdict`"
+ def _asdict(self):
+ return {}
+
+ @classmethod
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `_make`"
+ def _make(cls, iterable):
+ return cls(*iterable)
+```
+
+Classes that inherit from a `NamedTuple` class (but don't directly inherit from `NamedTuple`) are
+not subject to these restrictions:
+
+```py
+from typing import NamedTuple
+
+class Base(NamedTuple):
+ x: int
+
+class Child(Base):
+ # This is fine - Child is not directly a NamedTuple
+ _asdict = 42
+```
diff --git a/crates/ty_python_semantic/resources/mdtest/override.md b/crates/ty_python_semantic/resources/mdtest/override.md
index c1ab23d790..386db9b340 100644
--- a/crates/ty_python_semantic/resources/mdtest/override.md
+++ b/crates/ty_python_semantic/resources/mdtest/override.md
@@ -283,8 +283,7 @@ class MyNamedTuple(NamedTuple):
x: int
@override
- # TODO: this raises an exception at runtime (which we should emit a diagnostic for).
- # It shouldn't be an `invalid-explicit-override` diagnostic, however.
+ # error: [invalid-named-tuple] "Cannot overwrite NamedTuple attribute `_asdict`"
def _asdict(self, /) -> dict[str, Any]: ...
class MyNamedTupleParent(NamedTuple):
diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs
index f4f62a0f0b..5a5c990ab1 100644
--- a/crates/ty_python_semantic/src/types/diagnostic.rs
+++ b/crates/ty_python_semantic/src/types/diagnostic.rs
@@ -545,7 +545,8 @@ declare_lint! {
///
/// ## Why is this bad?
/// An invalidly defined `NamedTuple` class may lead to the type checker
- /// drawing incorrect conclusions. It may also lead to `TypeError`s at runtime.
+ /// drawing incorrect conclusions. It may also lead to `TypeError`s or
+ /// `AttributeError`s at runtime.
///
/// ## Examples
/// A class definition cannot combine `NamedTuple` with other base classes
@@ -558,6 +559,27 @@ declare_lint! {
/// >>> class Foo(NamedTuple, object): ...
/// TypeError: can only inherit from a NamedTuple type and Generic
/// ```
+ ///
+ /// Further, `NamedTuple` field names cannot start with an underscore:
+ ///
+ /// ```pycon
+ /// >>> from typing import NamedTuple
+ /// >>> class Foo(NamedTuple):
+ /// ... _bar: int
+ /// ValueError: Field names cannot start with an underscore: '_bar'
+ /// ```
+ ///
+ /// `NamedTuple` classes also have certain synthesized attributes (like `_asdict`, `_make`,
+ /// `_replace`, etc.) that cannot be overwritten. Attempting to assign to these attributes
+ /// without a type annotation will raise an `AttributeError` at runtime.
+ ///
+ /// ```pycon
+ /// >>> from typing import NamedTuple
+ /// >>> class Foo(NamedTuple):
+ /// ... x: int
+ /// ... _asdict = 42
+ /// AttributeError: Cannot overwrite NamedTuple attribute _asdict
+ /// ```
pub(crate) static INVALID_NAMED_TUPLE = {
summary: "detects invalid `NamedTuple` class definitions",
status: LintStatus::stable("0.0.1-alpha.19"),
diff --git a/crates/ty_python_semantic/src/types/overrides.rs b/crates/ty_python_semantic/src/types/overrides.rs
index 517878202a..a56f04d1db 100644
--- a/crates/ty_python_semantic/src/types/overrides.rs
+++ b/crates/ty_python_semantic/src/types/overrides.rs
@@ -11,20 +11,40 @@ use crate::{
Db,
lint::LintId,
place::Place,
- semantic_index::{place_table, scope::ScopeId, symbol::ScopedSymbolId, use_def_map},
+ semantic_index::{
+ definition::DefinitionKind, place_table, scope::ScopeId, symbol::ScopedSymbolId,
+ use_def_map,
+ },
types::{
ClassBase, ClassLiteral, ClassType, KnownClass, Type,
class::CodeGeneratorKind,
context::InferContext,
diagnostic::{
- INVALID_EXPLICIT_OVERRIDE, INVALID_METHOD_OVERRIDE, OVERRIDE_OF_FINAL_METHOD,
- report_invalid_method_override, report_overridden_final_method,
+ INVALID_EXPLICIT_OVERRIDE, INVALID_METHOD_OVERRIDE, INVALID_NAMED_TUPLE,
+ OVERRIDE_OF_FINAL_METHOD, report_invalid_method_override,
+ report_overridden_final_method,
},
function::{FunctionDecorators, FunctionType, KnownFunction},
ide_support::{MemberWithDefinition, all_declarations_and_bindings},
},
};
+/// Prohibited `NamedTuple` attributes that cannot be overwritten.
+/// See for the list.
+const PROHIBITED_NAMEDTUPLE_ATTRS: &[&str] = &[
+ "__new__",
+ "__init__",
+ "__slots__",
+ "__getnewargs__",
+ "_fields",
+ "_field_defaults",
+ "_field_types",
+ "_make",
+ "_replace",
+ "_asdict",
+ "_source",
+];
+
pub(super) fn check_class<'db>(context: &InferContext<'db, '_>, class: ClassLiteral<'db>) {
let db = context.db();
let configuration = OverrideRulesConfig::from(context);
@@ -126,6 +146,27 @@ fn check_class_declaration<'db>(
let (literal, specialization) = class.class_literal(db);
let class_kind = CodeGeneratorKind::from_class(db, literal, specialization);
+ // Check for prohibited `NamedTuple` attribute overrides.
+ //
+ // `NamedTuple` classes have certain synthesized attributes (like `_asdict`, `_make`, etc.)
+ // that cannot be overwritten. Attempting to assign to these attributes (without type
+ // annotations) or define methods with these names will raise an `AttributeError` at runtime.
+ if class_kind == Some(CodeGeneratorKind::NamedTuple)
+ && configuration.check_prohibited_named_tuple_attrs()
+ && PROHIBITED_NAMEDTUPLE_ATTRS.contains(&member.name.as_str())
+ && !matches!(definition.kind(db), DefinitionKind::AnnotatedAssignment(_))
+ && let Some(builder) = context.report_lint(
+ &INVALID_NAMED_TUPLE,
+ definition.focus_range(db, context.module()),
+ )
+ {
+ let mut diagnostic = builder.into_diagnostic(format_args!(
+ "Cannot overwrite NamedTuple attribute `{}`",
+ &member.name
+ ));
+ diagnostic.info("This will cause the class creation to fail at runtime");
+ }
+
let mut subclass_overrides_superclass_declaration = false;
let mut has_dynamic_superclass = false;
let mut has_typeddict_in_mro = false;
@@ -349,6 +390,7 @@ bitflags! {
const LISKOV_METHODS = 1 << 0;
const EXPLICIT_OVERRIDE = 1 << 1;
const FINAL_METHOD_OVERRIDDEN = 1 << 2;
+ const PROHIBITED_NAMED_TUPLE_ATTR = 1 << 3;
}
}
@@ -368,6 +410,9 @@ impl From<&InferContext<'_, '_>> for OverrideRulesConfig {
if rule_selection.is_enabled(LintId::of(&OVERRIDE_OF_FINAL_METHOD)) {
config |= OverrideRulesConfig::FINAL_METHOD_OVERRIDDEN;
}
+ if rule_selection.is_enabled(LintId::of(&INVALID_NAMED_TUPLE)) {
+ config |= OverrideRulesConfig::PROHIBITED_NAMED_TUPLE_ATTR;
+ }
config
}
@@ -385,4 +430,8 @@ impl OverrideRulesConfig {
const fn check_final_method_overridden(self) -> bool {
self.contains(OverrideRulesConfig::FINAL_METHOD_OVERRIDDEN)
}
+
+ const fn check_prohibited_named_tuple_attrs(self) -> bool {
+ self.contains(OverrideRulesConfig::PROHIBITED_NAMED_TUPLE_ATTR)
+ }
}
diff --git a/ty.schema.json b/ty.schema.json
index e2325d6ac4..5630ec353c 100644
--- a/ty.schema.json
+++ b/ty.schema.json
@@ -645,7 +645,7 @@
},
"invalid-named-tuple": {
"title": "detects invalid `NamedTuple` class definitions",
- "description": "## What it does\nChecks for invalidly defined `NamedTuple` classes.\n\n## Why is this bad?\nAn invalidly defined `NamedTuple` class may lead to the type checker\ndrawing incorrect conclusions. It may also lead to `TypeError`s at runtime.\n\n## Examples\nA class definition cannot combine `NamedTuple` with other base classes\nin multiple inheritance; doing so raises a `TypeError` at runtime. The sole\nexception to this rule is `Generic[]`, which can be used alongside `NamedTuple`\nin a class's bases list.\n\n```pycon\n>>> from typing import NamedTuple\n>>> class Foo(NamedTuple, object): ...\nTypeError: can only inherit from a NamedTuple type and Generic\n```",
+ "description": "## What it does\nChecks for invalidly defined `NamedTuple` classes.\n\n## Why is this bad?\nAn invalidly defined `NamedTuple` class may lead to the type checker\ndrawing incorrect conclusions. It may also lead to `TypeError`s or\n`AttributeError`s at runtime.\n\n## Examples\nA class definition cannot combine `NamedTuple` with other base classes\nin multiple inheritance; doing so raises a `TypeError` at runtime. The sole\nexception to this rule is `Generic[]`, which can be used alongside `NamedTuple`\nin a class's bases list.\n\n```pycon\n>>> from typing import NamedTuple\n>>> class Foo(NamedTuple, object): ...\nTypeError: can only inherit from a NamedTuple type and Generic\n```\n\nFurther, `NamedTuple` field names cannot start with an underscore:\n\n```pycon\n>>> from typing import NamedTuple\n>>> class Foo(NamedTuple):\n... _bar: int\nValueError: Field names cannot start with an underscore: '_bar'\n```\n\n`NamedTuple` classes also have certain synthesized attributes (like `_asdict`, `_make`,\n`_replace`, etc.) that cannot be overwritten. Attempting to assign to these attributes\nwithout a type annotation will raise an `AttributeError` at runtime.\n\n```pycon\n>>> from typing import NamedTuple\n>>> class Foo(NamedTuple):\n... x: int\n... _asdict = 42\nAttributeError: Cannot overwrite NamedTuple attribute _asdict\n```",
"default": "error",
"oneOf": [
{