From 77f3cb4baade9cf0a02bd779dde9b11766bda0d4 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 10 Jul 2020 11:50:36 +0300 Subject: [PATCH] code/code: type annotations & doc cleanups --- src/_pytest/_code/code.py | 180 ++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 95 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index ab38b204f..aa28fea18 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -51,7 +51,7 @@ if TYPE_CHECKING: class Code: - """ wrapper around Python code objects """ + """Wrapper around Python code objects.""" def __init__(self, rawcode) -> None: if not hasattr(rawcode, "co_filename"): @@ -74,7 +74,7 @@ class Code: @property def path(self) -> Union[py.path.local, str]: - """ return a path object pointing to source code (or a str in case + """Return a path object pointing to source code (or a str in case of OSError / non-existing file). """ if not self.raw.co_filename: @@ -92,24 +92,22 @@ class Code: @property def fullsource(self) -> Optional["Source"]: - """ return a _pytest._code.Source object for the full source file of the code - """ + """Return a _pytest._code.Source object for the full source file of the code.""" full, _ = findsource(self.raw) return full def source(self) -> "Source": - """ return a _pytest._code.Source object for the code object's source only - """ + """Return a _pytest._code.Source object for the code object's source only.""" # return source only for that part of code return Source(self.raw) def getargs(self, var: bool = False) -> Tuple[str, ...]: - """ return a tuple with the argument names for the code object + """Return a tuple with the argument names for the code object. - if 'var' is set True also return the names of the variable and - keyword arguments when present + If 'var' is set True also return the names of the variable and + keyword arguments when present. """ - # handfull shortcut for getting args + # Handy shortcut for getting args. raw = self.raw argcount = raw.co_argcount if var: @@ -131,44 +129,43 @@ class Frame: @property def statement(self) -> "Source": - """ statement this frame is at """ + """Statement this frame is at.""" if self.code.fullsource is None: return Source("") return self.code.fullsource.getstatement(self.lineno) def eval(self, code, **vars): - """ evaluate 'code' in the frame + """Evaluate 'code' in the frame. - 'vars' are optional additional local variables + 'vars' are optional additional local variables. - returns the result of the evaluation + Returns the result of the evaluation. """ f_locals = self.f_locals.copy() f_locals.update(vars) return eval(code, self.f_globals, f_locals) def exec_(self, code, **vars) -> None: - """ exec 'code' in the frame + """Exec 'code' in the frame. - 'vars' are optional; additional local variables + 'vars' are optional; additional local variables. """ f_locals = self.f_locals.copy() f_locals.update(vars) exec(code, self.f_globals, f_locals) def repr(self, object: object) -> str: - """ return a 'safe' (non-recursive, one-line) string repr for 'object' - """ + """Return a 'safe' (non-recursive, one-line) string repr for 'object'.""" return saferepr(object) def is_true(self, object): return object def getargs(self, var: bool = False): - """ return a list of tuples (name, value) for all arguments + """Return a list of tuples (name, value) for all arguments. - if 'var' is set True also include the variable and keyword - arguments when present + If 'var' is set True, also include the variable and keyword arguments + when present. """ retval = [] for arg in self.code.getargs(var): @@ -180,12 +177,16 @@ class Frame: class TracebackEntry: - """ a single entry in a traceback """ + """A single entry in a Traceback.""" _repr_style = None # type: Optional[Literal["short", "long"]] exprinfo = None - def __init__(self, rawentry: TracebackType, excinfo=None) -> None: + def __init__( + self, + rawentry: TracebackType, + excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None, + ) -> None: self._excinfo = excinfo self._rawentry = rawentry self.lineno = rawentry.tb_lineno - 1 @@ -207,26 +208,26 @@ class TracebackEntry: @property def statement(self) -> "Source": - """ _pytest._code.Source object for the current statement """ + """_pytest._code.Source object for the current statement.""" source = self.frame.code.fullsource assert source is not None return source.getstatement(self.lineno) @property def path(self) -> Union[py.path.local, str]: - """ path to the source code """ + """Path to the source code.""" return self.frame.code.path @property def locals(self) -> Dict[str, Any]: - """ locals of underlying frame """ + """Locals of underlying frame.""" return self.frame.f_locals def getfirstlinesource(self) -> int: return self.frame.code.firstlineno def getsource(self, astcache=None) -> Optional["Source"]: - """ return failing source code. """ + """Return failing source code.""" # we use the passed in astcache to not reparse asttrees # within exception info printing source = self.frame.code.fullsource @@ -251,19 +252,19 @@ class TracebackEntry: source = property(getsource) - def ishidden(self): - """ return True if the current frame has a var __tracebackhide__ - resolving to True. + def ishidden(self) -> bool: + """Return True if the current frame has a var __tracebackhide__ + resolving to True. - If __tracebackhide__ is a callable, it gets called with the - ExceptionInfo instance and can decide whether to hide the traceback. + If __tracebackhide__ is a callable, it gets called with the + ExceptionInfo instance and can decide whether to hide the traceback. - mostly for internal use + Mostly for internal use. """ f = self.frame tbh = f.f_locals.get( "__tracebackhide__", f.f_globals.get("__tracebackhide__", False) - ) + ) # type: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]] if tbh and callable(tbh): return tbh(None if self._excinfo is None else self._excinfo()) return tbh @@ -280,21 +281,19 @@ class TracebackEntry: @property def name(self) -> str: - """ co_name of underlying code """ + """co_name of underlying code.""" return self.frame.code.raw.co_name class Traceback(List[TracebackEntry]): - """ Traceback objects encapsulate and offer higher level - access to Traceback entries. - """ + """Traceback objects encapsulate and offer higher level access to Traceback entries.""" def __init__( self, tb: Union[TracebackType, Iterable[TracebackEntry]], - excinfo: Optional["ReferenceType[ExceptionInfo]"] = None, + excinfo: Optional["ReferenceType[ExceptionInfo[BaseException]]"] = None, ) -> None: - """ initialize from given python traceback object and ExceptionInfo """ + """Initialize from given python traceback object and ExceptionInfo.""" self._excinfo = excinfo if isinstance(tb, TracebackType): @@ -313,16 +312,16 @@ class Traceback(List[TracebackEntry]): path=None, lineno: Optional[int] = None, firstlineno: Optional[int] = None, - excludepath=None, + excludepath: Optional[py.path.local] = None, ) -> "Traceback": - """ return a Traceback instance wrapping part of this Traceback + """Return a Traceback instance wrapping part of this Traceback. - by providing any combination of path, lineno and firstlineno, the - first frame to start the to-be-returned traceback is determined + By providing any combination of path, lineno and firstlineno, the + first frame to start the to-be-returned traceback is determined. - this allows cutting the first part of a Traceback instance e.g. - for formatting reasons (removing some uninteresting bits that deal - with handling of the exception/traceback) + This allows cutting the first part of a Traceback instance e.g. + for formatting reasons (removing some uninteresting bits that deal + with handling of the exception/traceback). """ for x in self: code = x.frame.code @@ -359,21 +358,19 @@ class Traceback(List[TracebackEntry]): def filter( self, fn: Callable[[TracebackEntry], bool] = lambda x: not x.ishidden() ) -> "Traceback": - """ return a Traceback instance with certain items removed + """Return a Traceback instance with certain items removed - fn is a function that gets a single argument, a TracebackEntry - instance, and should return True when the item should be added - to the Traceback, False when not + fn is a function that gets a single argument, a TracebackEntry + instance, and should return True when the item should be added + to the Traceback, False when not. - by default this removes all the TracebackEntries which are hidden - (see ishidden() above) + By default this removes all the TracebackEntries which are hidden + (see ishidden() above). """ return Traceback(filter(fn, self), self._excinfo) def getcrashentry(self) -> TracebackEntry: - """ return last non-hidden traceback entry that lead - to the exception of a traceback. - """ + """Return last non-hidden traceback entry that lead to the exception of a traceback.""" for i in range(-1, -len(self) - 1, -1): entry = self[i] if not entry.ishidden(): @@ -381,9 +378,8 @@ class Traceback(List[TracebackEntry]): return self[-1] def recursionindex(self) -> Optional[int]: - """ return the index of the frame/TracebackEntry where recursion - originates if appropriate, None if no recursion occurred - """ + """Return the index of the frame/TracebackEntry where recursion originates if + appropriate, None if no recursion occurred.""" cache = {} # type: Dict[Tuple[Any, int, int], List[Dict[str, Any]]] for i, entry in enumerate(self): # id for the code.raw is needed to work around @@ -414,14 +410,12 @@ co_equal = compile( ) -_E = TypeVar("_E", bound=BaseException) +_E = TypeVar("_E", bound=BaseException, covariant=True) @attr.s(repr=False) class ExceptionInfo(Generic[_E]): - """ wraps sys.exc_info() objects and offers - help for navigating the traceback. - """ + """Wraps sys.exc_info() objects and offers help for navigating the traceback.""" _assert_start_repr = "AssertionError('assert " @@ -435,13 +429,12 @@ class ExceptionInfo(Generic[_E]): exc_info: Tuple["Type[_E]", "_E", TracebackType], exprinfo: Optional[str] = None, ) -> "ExceptionInfo[_E]": - """returns an ExceptionInfo for an existing exc_info tuple. + """Returns an ExceptionInfo for an existing exc_info tuple. .. warning:: Experimental API - :param exprinfo: a text string helping to determine if we should strip ``AssertionError`` from the output, defaults to the exception message/``__str__()`` @@ -460,13 +453,12 @@ class ExceptionInfo(Generic[_E]): def from_current( cls, exprinfo: Optional[str] = None ) -> "ExceptionInfo[BaseException]": - """returns an ExceptionInfo matching the current traceback + """Returns an ExceptionInfo matching the current traceback. .. warning:: Experimental API - :param exprinfo: a text string helping to determine if we should strip ``AssertionError`` from the output, defaults to the exception message/``__str__()`` @@ -480,8 +472,7 @@ class ExceptionInfo(Generic[_E]): @classmethod def for_later(cls) -> "ExceptionInfo[_E]": - """return an unfilled ExceptionInfo - """ + """Return an unfilled ExceptionInfo.""" return cls(None) def fill_unfilled(self, exc_info: Tuple["Type[_E]", _E, TracebackType]) -> None: @@ -491,7 +482,7 @@ class ExceptionInfo(Generic[_E]): @property def type(self) -> "Type[_E]": - """the exception class""" + """The exception class.""" assert ( self._excinfo is not None ), ".type can only be used after the context manager exits" @@ -499,7 +490,7 @@ class ExceptionInfo(Generic[_E]): @property def value(self) -> _E: - """the exception value""" + """The exception value.""" assert ( self._excinfo is not None ), ".value can only be used after the context manager exits" @@ -507,7 +498,7 @@ class ExceptionInfo(Generic[_E]): @property def tb(self) -> TracebackType: - """the exception raw traceback""" + """The exception raw traceback.""" assert ( self._excinfo is not None ), ".tb can only be used after the context manager exits" @@ -515,7 +506,7 @@ class ExceptionInfo(Generic[_E]): @property def typename(self) -> str: - """the type name of the exception""" + """The type name of the exception.""" assert ( self._excinfo is not None ), ".typename can only be used after the context manager exits" @@ -523,7 +514,7 @@ class ExceptionInfo(Generic[_E]): @property def traceback(self) -> Traceback: - """the traceback""" + """The traceback.""" if self._traceback is None: self._traceback = Traceback(self.tb, excinfo=ref(self)) return self._traceback @@ -540,12 +531,12 @@ class ExceptionInfo(Generic[_E]): ) def exconly(self, tryshort: bool = False) -> str: - """ return the exception as a string + """Return the exception as a string. - when 'tryshort' resolves to True, and the exception is a - _pytest._code._AssertionError, only the actual exception part of - the exception representation is returned (so 'AssertionError: ' is - removed from the beginning) + When 'tryshort' resolves to True, and the exception is a + _pytest._code._AssertionError, only the actual exception part of + the exception representation is returned (so 'AssertionError: ' is + removed from the beginning). """ lines = format_exception_only(self.type, self.value) text = "".join(lines) @@ -580,8 +571,7 @@ class ExceptionInfo(Generic[_E]): truncate_locals: bool = True, chain: bool = True, ) -> Union["ReprExceptionInfo", "ExceptionChainRepr"]: - """ - Return str()able representation of this exception info. + """Return str()able representation of this exception info. :param bool showlocals: Show locals per traceback entry. @@ -630,11 +620,10 @@ class ExceptionInfo(Generic[_E]): return fmt.repr_excinfo(self) def match(self, regexp: "Union[str, Pattern]") -> "Literal[True]": - """ - Check whether the regular expression `regexp` matches the string + """Check whether the regular expression `regexp` matches the string representation of the exception using :func:`python:re.search`. - If it matches `True` is returned. - If it doesn't match an `AssertionError` is raised. + + If it matches `True` is returned, otherwise an `AssertionError` is raised. """ __tracebackhide__ = True assert re.search( @@ -646,7 +635,7 @@ class ExceptionInfo(Generic[_E]): @attr.s class FormattedExcinfo: - """ presenting information about failing Functions and Generators. """ + """Presenting information about failing Functions and Generators.""" # for traceback entries flow_marker = ">" @@ -697,7 +686,7 @@ class FormattedExcinfo: excinfo: Optional[ExceptionInfo] = None, short: bool = False, ) -> List[str]: - """ return formatted and marked up source lines. """ + """Return formatted and marked up source lines.""" lines = [] if source is None or line_index >= len(source.lines): source = Source("???") @@ -938,7 +927,7 @@ class ExceptionRepr(TerminalRepr): reprcrash = None # type: Optional[ReprFileLocation] reprtraceback = None # type: ReprTraceback - def __attrs_post_init__(self): + def __attrs_post_init__(self) -> None: self.sections = [] # type: List[Tuple[str, str, str]] def addsection(self, name: str, content: str, sep: str = "-") -> None: @@ -958,7 +947,7 @@ class ExceptionChainRepr(ExceptionRepr): ] ) - def __attrs_post_init__(self): + def __attrs_post_init__(self) -> None: super().__attrs_post_init__() # reprcrash and reprtraceback of the outermost (the newest) exception # in the chain @@ -1160,8 +1149,9 @@ class ReprFuncArgs(TerminalRepr): tw.line("") -def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]: - """ Return source location (path, lineno) for the given object. +def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]: + """Return source location (path, lineno) for the given object. + If the source cannot be determined return ("", -1). The line number is 0-based. @@ -1171,13 +1161,13 @@ def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]: # in 6ec13a2b9. It ("place_as") appears to be something very custom. obj = get_real_func(obj) if hasattr(obj, "place_as"): - obj = obj.place_as + obj = obj.place_as # type: ignore[attr-defined] try: code = Code(obj) except TypeError: try: - fn = inspect.getsourcefile(obj) or inspect.getfile(obj) + fn = inspect.getsourcefile(obj) or inspect.getfile(obj) # type: ignore[arg-type] except TypeError: return "", -1 @@ -1189,8 +1179,8 @@ def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]: except OSError: pass return fspath, lineno - else: - return code.path, code.firstlineno + + return code.path, code.firstlineno # relative paths that we use to filter traceback entries from appearing to the user;