Skip to content

Commit

Permalink
Merge pull request #7171 from bluetech/code-import-cycles
Browse files Browse the repository at this point in the history
code: fix import cycles between code.py and source.py
  • Loading branch information
bluetech committed May 6, 2020
2 parents 8c2c297 + 69143fe commit d16ae0b
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 86 deletions.
32 changes: 22 additions & 10 deletions src/_pytest/_code/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
""" python inspection/code generation API """
from .code import Code # noqa
from .code import ExceptionInfo # noqa
from .code import filter_traceback # noqa
from .code import Frame # noqa
from .code import getrawcode # noqa
from .code import Traceback # noqa
from .source import compile_ as compile # noqa
from .source import getfslineno # noqa
from .source import Source # noqa
"""Python inspection/code generation API."""
from .code import Code
from .code import ExceptionInfo
from .code import filter_traceback
from .code import Frame
from .code import getfslineno
from .code import getrawcode
from .code import Traceback
from .source import compile_ as compile
from .source import Source

__all__ = [
"Code",
"ExceptionInfo",
"filter_traceback",
"Frame",
"getfslineno",
"getrawcode",
"Traceback",
"compile",
"Source",
]
71 changes: 40 additions & 31 deletions src/_pytest/_code/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,15 @@
import py

import _pytest
from _pytest._code.source import findsource
from _pytest._code.source import getrawcode
from _pytest._code.source import getstatementrange_ast
from _pytest._code.source import Source
from _pytest._io import TerminalWriter
from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr
from _pytest.compat import ATTRS_EQ_FIELD
from _pytest.compat import get_real_func
from _pytest.compat import overload
from _pytest.compat import TYPE_CHECKING

Expand All @@ -41,8 +46,6 @@
from typing_extensions import Literal
from weakref import ReferenceType # noqa: F401

from _pytest._code import Source

_TracebackStyle = Literal["long", "short", "line", "no", "native"]


Expand Down Expand Up @@ -90,18 +93,14 @@ def path(self) -> Union[py.path.local, str]:
def fullsource(self) -> Optional["Source"]:
""" return a _pytest._code.Source object for the full source file of the code
"""
from _pytest._code import source

full, _ = source.findsource(self.raw)
full, _ = findsource(self.raw)
return full

def source(self) -> "Source":
""" return a _pytest._code.Source object for the code object's source only
"""
# return source only for that part of code
import _pytest._code

return _pytest._code.Source(self.raw)
return Source(self.raw)

def getargs(self, var: bool = False) -> Tuple[str, ...]:
""" return a tuple with the argument names for the code object
Expand Down Expand Up @@ -132,10 +131,8 @@ def __init__(self, frame: FrameType) -> None:
@property
def statement(self) -> "Source":
""" statement this frame is at """
import _pytest._code

if self.code.fullsource is None:
return _pytest._code.Source("")
return Source("")
return self.code.fullsource.getstatement(self.lineno)

def eval(self, code, **vars):
Expand Down Expand Up @@ -231,8 +228,6 @@ def getsource(self, astcache=None) -> Optional["Source"]:
""" return failing source code. """
# we use the passed in astcache to not reparse asttrees
# within exception info printing
from _pytest._code.source import getstatementrange_ast

source = self.frame.code.fullsource
if source is None:
return None
Expand Down Expand Up @@ -703,11 +698,9 @@ def get_source(
short: bool = False,
) -> List[str]:
""" return formatted and marked up source lines. """
import _pytest._code

lines = []
if source is None or line_index >= len(source.lines):
source = _pytest._code.Source("???")
source = Source("???")
line_index = 0
if line_index < 0:
line_index += len(source)
Expand Down Expand Up @@ -769,11 +762,9 @@ def repr_locals(self, locals: Dict[str, object]) -> Optional["ReprLocals"]:
def repr_traceback_entry(
self, entry: TracebackEntry, excinfo: Optional[ExceptionInfo] = None
) -> "ReprEntry":
import _pytest._code

source = self._getentrysource(entry)
if source is None:
source = _pytest._code.Source("???")
source = Source("???")
line_index = 0
else:
line_index = entry.lineno - entry.getfirstlinesource()
Expand Down Expand Up @@ -1150,19 +1141,37 @@ def toterminal(self, tw: TerminalWriter) -> None:
tw.line("")


def getrawcode(obj, trycall: bool = True):
""" return code object for given function. """
def getfslineno(obj: Any) -> 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.
"""
# xxx let decorators etc specify a sane ordering
# NOTE: this used to be done in _pytest.compat.getfslineno, initially added
# 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

try:
return obj.__code__
except AttributeError:
obj = getattr(obj, "f_code", obj)
obj = getattr(obj, "__code__", obj)
if trycall and not hasattr(obj, "co_firstlineno"):
if hasattr(obj, "__call__") and not inspect.isclass(obj):
x = getrawcode(obj.__call__, trycall=False)
if hasattr(x, "co_firstlineno"):
return x
return obj
code = Code(obj)
except TypeError:
try:
fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
except TypeError:
return "", -1

fspath = fn and py.path.local(fn) or ""
lineno = -1
if fspath:
try:
_, lineno = findsource(obj)
except OSError:
pass
return fspath, lineno
else:
return code.path, code.firstlineno


# relative paths that we use to filter traceback entries from appearing to the user;
Expand Down
56 changes: 15 additions & 41 deletions src/_pytest/_code/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from bisect import bisect_right
from types import CodeType
from types import FrameType
from typing import Any
from typing import Iterator
from typing import List
from typing import Optional
Expand All @@ -18,7 +17,6 @@

import py

from _pytest.compat import get_real_func
from _pytest.compat import overload
from _pytest.compat import TYPE_CHECKING

Expand Down Expand Up @@ -279,41 +277,6 @@ def compile_( # noqa: F811
return s.compile(filename, mode, flags, _genframe=_genframe)


def getfslineno(obj: Any) -> 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.
"""
from .code import Code

# xxx let decorators etc specify a sane ordering
# NOTE: this used to be done in _pytest.compat.getfslineno, initially added
# 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

try:
code = Code(obj)
except TypeError:
try:
fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
except TypeError:
return "", -1

fspath = fn and py.path.local(fn) or ""
lineno = -1
if fspath:
try:
_, lineno = findsource(obj)
except OSError:
pass
return fspath, lineno
else:
return code.path, code.firstlineno


#
# helper functions
#
Expand All @@ -329,9 +292,22 @@ def findsource(obj) -> Tuple[Optional[Source], int]:
return source, lineno


def getsource(obj, **kwargs) -> Source:
from .code import getrawcode
def getrawcode(obj, trycall: bool = True):
""" return code object for given function. """
try:
return obj.__code__
except AttributeError:
obj = getattr(obj, "f_code", obj)
obj = getattr(obj, "__code__", obj)
if trycall and not hasattr(obj, "co_firstlineno"):
if hasattr(obj, "__call__") and not inspect.isclass(obj):
x = getrawcode(obj.__call__, trycall=False)
if hasattr(x, "co_firstlineno"):
return x
return obj


def getsource(obj, **kwargs) -> Source:
obj = getrawcode(obj)
try:
strsrc = inspect.getsource(obj)
Expand All @@ -346,8 +322,6 @@ def deindent(lines: Sequence[str]) -> List[str]:


def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[int]]:
import ast

# flatten all statements and except handlers into one lineno-list
# AST's line numbers start indexing at 1
values = [] # type: List[int]
Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
import py

import _pytest
from _pytest._code import getfslineno
from _pytest._code.code import FormattedExcinfo
from _pytest._code.code import TerminalRepr
from _pytest._code.source import getfslineno
from _pytest._io import TerminalWriter
from _pytest.compat import _format_args
from _pytest.compat import _PytestWrapper
Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/mark/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import attr

from .._code.source import getfslineno
from .._code import getfslineno
from ..compat import ascii_escaped
from ..compat import NOTSET
from _pytest.outcomes import fail
Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
import py

import _pytest._code
from _pytest._code import getfslineno
from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo
from _pytest._code.code import ReprExceptionInfo
from _pytest._code.source import getfslineno
from _pytest.compat import cached_property
from _pytest.compat import TYPE_CHECKING
from _pytest.config import Config
Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
from _pytest import fixtures
from _pytest import nodes
from _pytest._code import filter_traceback
from _pytest._code import getfslineno
from _pytest._code.code import ExceptionInfo
from _pytest._code.source import getfslineno
from _pytest._io import TerminalWriter
from _pytest._io.saferepr import saferepr
from _pytest.compat import ascii_escaped
Expand Down

0 comments on commit d16ae0b

Please sign in to comment.