| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- """Various utilities that don't depend on other modules in mypyc.irbuild."""
- from __future__ import annotations
- from typing import Any
- from mypy.nodes import (
- ARG_NAMED,
- ARG_NAMED_OPT,
- ARG_OPT,
- ARG_POS,
- GDEF,
- ArgKind,
- BytesExpr,
- CallExpr,
- ClassDef,
- Decorator,
- Expression,
- FloatExpr,
- FuncDef,
- IntExpr,
- NameExpr,
- OverloadedFuncDef,
- RefExpr,
- StrExpr,
- TupleExpr,
- UnaryExpr,
- Var,
- )
- DATACLASS_DECORATORS = {"dataclasses.dataclass", "attr.s", "attr.attrs"}
- def is_trait_decorator(d: Expression) -> bool:
- return isinstance(d, RefExpr) and d.fullname == "mypy_extensions.trait"
- def is_trait(cdef: ClassDef) -> bool:
- return any(is_trait_decorator(d) for d in cdef.decorators) or cdef.info.is_protocol
- def dataclass_decorator_type(d: Expression) -> str | None:
- if isinstance(d, RefExpr) and d.fullname in DATACLASS_DECORATORS:
- return d.fullname.split(".")[0]
- elif (
- isinstance(d, CallExpr)
- and isinstance(d.callee, RefExpr)
- and d.callee.fullname in DATACLASS_DECORATORS
- ):
- name = d.callee.fullname.split(".")[0]
- if name == "attr" and "auto_attribs" in d.arg_names:
- # Note: the mypy attrs plugin checks that the value of auto_attribs is
- # not computed at runtime, so we don't need to perform that check here
- auto = d.args[d.arg_names.index("auto_attribs")]
- if isinstance(auto, NameExpr) and auto.name == "True":
- return "attr-auto"
- return name
- else:
- return None
- def is_dataclass_decorator(d: Expression) -> bool:
- return dataclass_decorator_type(d) is not None
- def is_dataclass(cdef: ClassDef) -> bool:
- return any(is_dataclass_decorator(d) for d in cdef.decorators)
- def dataclass_type(cdef: ClassDef) -> str | None:
- for d in cdef.decorators:
- typ = dataclass_decorator_type(d)
- if typ is not None:
- return typ
- return None
- def get_mypyc_attr_literal(e: Expression) -> Any:
- """Convert an expression from a mypyc_attr decorator to a value.
- Supports a pretty limited range."""
- if isinstance(e, (StrExpr, IntExpr, FloatExpr)):
- return e.value
- elif isinstance(e, RefExpr) and e.fullname == "builtins.True":
- return True
- elif isinstance(e, RefExpr) and e.fullname == "builtins.False":
- return False
- elif isinstance(e, RefExpr) and e.fullname == "builtins.None":
- return None
- return NotImplemented
- def get_mypyc_attr_call(d: Expression) -> CallExpr | None:
- """Check if an expression is a call to mypyc_attr and return it if so."""
- if (
- isinstance(d, CallExpr)
- and isinstance(d.callee, RefExpr)
- and d.callee.fullname == "mypy_extensions.mypyc_attr"
- ):
- return d
- return None
- def get_mypyc_attrs(stmt: ClassDef | Decorator) -> dict[str, Any]:
- """Collect all the mypyc_attr attributes on a class definition or a function."""
- attrs: dict[str, Any] = {}
- for dec in stmt.decorators:
- d = get_mypyc_attr_call(dec)
- if d:
- for name, arg in zip(d.arg_names, d.args):
- if name is None:
- if isinstance(arg, StrExpr):
- attrs[arg.value] = True
- else:
- attrs[name] = get_mypyc_attr_literal(arg)
- return attrs
- def is_extension_class(cdef: ClassDef) -> bool:
- if any(
- not is_trait_decorator(d) and not is_dataclass_decorator(d) and not get_mypyc_attr_call(d)
- for d in cdef.decorators
- ):
- return False
- if cdef.info.typeddict_type:
- return False
- if cdef.info.is_named_tuple:
- return False
- if cdef.info.metaclass_type and cdef.info.metaclass_type.type.fullname not in (
- "abc.ABCMeta",
- "typing.TypingMeta",
- "typing.GenericMeta",
- ):
- return False
- return True
- def get_func_def(op: FuncDef | Decorator | OverloadedFuncDef) -> FuncDef:
- if isinstance(op, OverloadedFuncDef):
- assert op.impl
- op = op.impl
- if isinstance(op, Decorator):
- op = op.func
- return op
- def concrete_arg_kind(kind: ArgKind) -> ArgKind:
- """Find the concrete version of an arg kind that is being passed."""
- if kind == ARG_OPT:
- return ARG_POS
- elif kind == ARG_NAMED_OPT:
- return ARG_NAMED
- else:
- return kind
- def is_constant(e: Expression) -> bool:
- """Check whether we allow an expression to appear as a default value.
- We don't currently properly support storing the evaluated
- values for default arguments and default attribute values, so
- we restrict what expressions we allow. We allow literals of
- primitives types, None, and references to Final global
- variables.
- """
- return (
- isinstance(e, (StrExpr, BytesExpr, IntExpr, FloatExpr))
- or (isinstance(e, UnaryExpr) and e.op == "-" and isinstance(e.expr, (IntExpr, FloatExpr)))
- or (isinstance(e, TupleExpr) and all(is_constant(e) for e in e.items))
- or (
- isinstance(e, RefExpr)
- and e.kind == GDEF
- and (
- e.fullname in ("builtins.True", "builtins.False", "builtins.None")
- or (isinstance(e.node, Var) and e.node.is_final)
- )
- )
- )
- def bytes_from_str(value: str) -> bytes:
- """Convert a string representing bytes into actual bytes.
- This is needed because the literal characters of BytesExpr (the
- characters inside b'') are stored in BytesExpr.value, whose type is
- 'str' not 'bytes'.
- """
- return bytes(value, "utf8").decode("unicode-escape").encode("raw-unicode-escape")
|