This commit is contained in:
Christian Mantha
2026-03-02 19:10:52 -05:00
commit 2ca0b9ef7c
28907 changed files with 5233713 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
"""
Get information about what a frame is currently doing. Typical usage:
import executing
node = executing.Source.executing(frame).node
# node will be an AST node or None
"""
from collections import namedtuple
_VersionInfo = namedtuple('_VersionInfo', ('major', 'minor', 'micro'))
from .executing import Source, Executing, only, NotOneValueFound, cache, future_flags
from ._pytest_utils import is_pytest_compatible
try:
from .version import __version__ # type: ignore[import]
if "dev" in __version__:
raise ValueError
except Exception:
# version.py is auto-generated with the git tag when building
__version__ = "???"
__version_info__ = _VersionInfo(-1, -1, -1)
else:
__version_info__ = _VersionInfo(*map(int, __version__.split('.')))
__all__ = ["Source","is_pytest_compatible"]

View File

@@ -0,0 +1,22 @@
class KnownIssue(Exception):
"""
Raised in case of an known problem. Mostly because of cpython bugs.
Executing.node gets set to None in this case.
"""
pass
class VerifierFailure(Exception):
"""
Thrown for an unexpected mapping from instruction to ast node
Executing.node gets set to None in this case.
"""
def __init__(self, title, node, instruction):
# type: (object, object, object) -> None
self.node = node
self.instruction = instruction
super().__init__(title) # type: ignore[call-arg]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
import sys
def is_pytest_compatible() -> bool:
""" returns true if executing can be used for expressions inside assert statements which are rewritten by pytest
"""
if sys.version_info < (3, 11):
return False
try:
import pytest
except ImportError:
return False
return pytest.version_tuple >= (8, 3, 4)

View File

@@ -0,0 +1,139 @@
import ast
import sys
import dis
from typing import cast, Any,Iterator
import types
def assert_(condition, message=""):
# type: (Any, str) -> None
"""
Like an assert statement, but unaffected by -O
:param condition: value that is expected to be truthy
:type message: Any
"""
if not condition:
raise AssertionError(str(message))
if sys.version_info >= (3, 4):
# noinspection PyUnresolvedReferences
_get_instructions = dis.get_instructions
from dis import Instruction as _Instruction
class Instruction(_Instruction):
lineno = None # type: int
else:
from collections import namedtuple
class Instruction(namedtuple('Instruction', 'offset argval opname starts_line')):
lineno = None # type: int
from dis import HAVE_ARGUMENT, EXTENDED_ARG, hasconst, opname, findlinestarts, hasname
# Based on dis.disassemble from 2.7
# Left as similar as possible for easy diff
def _get_instructions(co):
# type: (types.CodeType) -> Iterator[Instruction]
code = co.co_code
linestarts = dict(findlinestarts(co))
n = len(code)
i = 0
extended_arg = 0
while i < n:
offset = i
c = code[i]
op = ord(c)
lineno = linestarts.get(i)
argval = None
i = i + 1
if op >= HAVE_ARGUMENT:
oparg = ord(code[i]) + ord(code[i + 1]) * 256 + extended_arg
extended_arg = 0
i = i + 2
if op == EXTENDED_ARG:
extended_arg = oparg * 65536
if op in hasconst:
argval = co.co_consts[oparg]
elif op in hasname:
argval = co.co_names[oparg]
elif opname[op] == 'LOAD_FAST':
argval = co.co_varnames[oparg]
yield Instruction(offset, argval, opname[op], lineno)
def get_instructions(co):
# type: (types.CodeType) -> Iterator[EnhancedInstruction]
lineno = co.co_firstlineno
for inst in _get_instructions(co):
inst = cast(EnhancedInstruction, inst)
lineno = inst.starts_line or lineno
assert_(lineno)
inst.lineno = lineno
yield inst
# Type class used to expand out the definition of AST to include fields added by this library
# It's not actually used for anything other than type checking though!
class EnhancedAST(ast.AST):
parent = None # type: EnhancedAST
# Type class used to expand out the definition of AST to include fields added by this library
# It's not actually used for anything other than type checking though!
class EnhancedInstruction(Instruction):
_copied = None # type: bool
def mangled_name(node):
# type: (EnhancedAST) -> str
"""
Parameters:
node: the node which should be mangled
name: the name of the node
Returns:
The mangled name of `node`
"""
function_class_types=(ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)
if isinstance(node, ast.Attribute):
name = node.attr
elif isinstance(node, ast.Name):
name = node.id
elif isinstance(node, (ast.alias)):
name = node.asname or node.name.split(".")[0]
elif isinstance(node, function_class_types):
name = node.name
elif isinstance(node, ast.ExceptHandler):
assert node.name
name = node.name
elif sys.version_info >= (3,12) and isinstance(node,ast.TypeVar):
name=node.name
else:
raise TypeError("no node to mangle")
if name.startswith("__") and not name.endswith("__"):
parent,child=node.parent,node
while not (isinstance(parent,ast.ClassDef) and child not in parent.bases):
if not hasattr(parent,"parent"):
break # pragma: no mutate
parent,child=parent.parent,parent
else:
class_name=parent.name.lstrip("_")
if class_name!="" and child not in parent.decorator_list:
return "_" + class_name + name
return name

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
__version__ = '2.2.1'