Skip to content

Commit d16ae0b

Browse files
authored
Merge pull request #7171 from bluetech/code-import-cycles
code: fix import cycles between code.py and source.py
2 parents 8c2c297 + 69143fe commit d16ae0b

File tree

7 files changed

+81
-86
lines changed

7 files changed

+81
-86
lines changed

src/_pytest/_code/__init__.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1-
""" python inspection/code generation API """
2-
from .code import Code # noqa
3-
from .code import ExceptionInfo # noqa
4-
from .code import filter_traceback # noqa
5-
from .code import Frame # noqa
6-
from .code import getrawcode # noqa
7-
from .code import Traceback # noqa
8-
from .source import compile_ as compile # noqa
9-
from .source import getfslineno # noqa
10-
from .source import Source # noqa
1+
"""Python inspection/code generation API."""
2+
from .code import Code
3+
from .code import ExceptionInfo
4+
from .code import filter_traceback
5+
from .code import Frame
6+
from .code import getfslineno
7+
from .code import getrawcode
8+
from .code import Traceback
9+
from .source import compile_ as compile
10+
from .source import Source
11+
12+
__all__ = [
13+
"Code",
14+
"ExceptionInfo",
15+
"filter_traceback",
16+
"Frame",
17+
"getfslineno",
18+
"getrawcode",
19+
"Traceback",
20+
"compile",
21+
"Source",
22+
]

src/_pytest/_code/code.py

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,15 @@
2929
import py
3030

3131
import _pytest
32+
from _pytest._code.source import findsource
33+
from _pytest._code.source import getrawcode
34+
from _pytest._code.source import getstatementrange_ast
35+
from _pytest._code.source import Source
3236
from _pytest._io import TerminalWriter
3337
from _pytest._io.saferepr import safeformat
3438
from _pytest._io.saferepr import saferepr
3539
from _pytest.compat import ATTRS_EQ_FIELD
40+
from _pytest.compat import get_real_func
3641
from _pytest.compat import overload
3742
from _pytest.compat import TYPE_CHECKING
3843

@@ -41,8 +46,6 @@
4146
from typing_extensions import Literal
4247
from weakref import ReferenceType # noqa: F401
4348

44-
from _pytest._code import Source
45-
4649
_TracebackStyle = Literal["long", "short", "line", "no", "native"]
4750

4851

@@ -90,18 +93,14 @@ def path(self) -> Union[py.path.local, str]:
9093
def fullsource(self) -> Optional["Source"]:
9194
""" return a _pytest._code.Source object for the full source file of the code
9295
"""
93-
from _pytest._code import source
94-
95-
full, _ = source.findsource(self.raw)
96+
full, _ = findsource(self.raw)
9697
return full
9798

9899
def source(self) -> "Source":
99100
""" return a _pytest._code.Source object for the code object's source only
100101
"""
101102
# return source only for that part of code
102-
import _pytest._code
103-
104-
return _pytest._code.Source(self.raw)
103+
return Source(self.raw)
105104

106105
def getargs(self, var: bool = False) -> Tuple[str, ...]:
107106
""" return a tuple with the argument names for the code object
@@ -132,10 +131,8 @@ def __init__(self, frame: FrameType) -> None:
132131
@property
133132
def statement(self) -> "Source":
134133
""" statement this frame is at """
135-
import _pytest._code
136-
137134
if self.code.fullsource is None:
138-
return _pytest._code.Source("")
135+
return Source("")
139136
return self.code.fullsource.getstatement(self.lineno)
140137

141138
def eval(self, code, **vars):
@@ -231,8 +228,6 @@ def getsource(self, astcache=None) -> Optional["Source"]:
231228
""" return failing source code. """
232229
# we use the passed in astcache to not reparse asttrees
233230
# within exception info printing
234-
from _pytest._code.source import getstatementrange_ast
235-
236231
source = self.frame.code.fullsource
237232
if source is None:
238233
return None
@@ -703,11 +698,9 @@ def get_source(
703698
short: bool = False,
704699
) -> List[str]:
705700
""" return formatted and marked up source lines. """
706-
import _pytest._code
707-
708701
lines = []
709702
if source is None or line_index >= len(source.lines):
710-
source = _pytest._code.Source("???")
703+
source = Source("???")
711704
line_index = 0
712705
if line_index < 0:
713706
line_index += len(source)
@@ -769,11 +762,9 @@ def repr_locals(self, locals: Dict[str, object]) -> Optional["ReprLocals"]:
769762
def repr_traceback_entry(
770763
self, entry: TracebackEntry, excinfo: Optional[ExceptionInfo] = None
771764
) -> "ReprEntry":
772-
import _pytest._code
773-
774765
source = self._getentrysource(entry)
775766
if source is None:
776-
source = _pytest._code.Source("???")
767+
source = Source("???")
777768
line_index = 0
778769
else:
779770
line_index = entry.lineno - entry.getfirstlinesource()
@@ -1150,19 +1141,37 @@ def toterminal(self, tw: TerminalWriter) -> None:
11501141
tw.line("")
11511142

11521143

1153-
def getrawcode(obj, trycall: bool = True):
1154-
""" return code object for given function. """
1144+
def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]:
1145+
""" Return source location (path, lineno) for the given object.
1146+
If the source cannot be determined return ("", -1).
1147+
1148+
The line number is 0-based.
1149+
"""
1150+
# xxx let decorators etc specify a sane ordering
1151+
# NOTE: this used to be done in _pytest.compat.getfslineno, initially added
1152+
# in 6ec13a2b9. It ("place_as") appears to be something very custom.
1153+
obj = get_real_func(obj)
1154+
if hasattr(obj, "place_as"):
1155+
obj = obj.place_as
1156+
11551157
try:
1156-
return obj.__code__
1157-
except AttributeError:
1158-
obj = getattr(obj, "f_code", obj)
1159-
obj = getattr(obj, "__code__", obj)
1160-
if trycall and not hasattr(obj, "co_firstlineno"):
1161-
if hasattr(obj, "__call__") and not inspect.isclass(obj):
1162-
x = getrawcode(obj.__call__, trycall=False)
1163-
if hasattr(x, "co_firstlineno"):
1164-
return x
1165-
return obj
1158+
code = Code(obj)
1159+
except TypeError:
1160+
try:
1161+
fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
1162+
except TypeError:
1163+
return "", -1
1164+
1165+
fspath = fn and py.path.local(fn) or ""
1166+
lineno = -1
1167+
if fspath:
1168+
try:
1169+
_, lineno = findsource(obj)
1170+
except OSError:
1171+
pass
1172+
return fspath, lineno
1173+
else:
1174+
return code.path, code.firstlineno
11661175

11671176

11681177
# relative paths that we use to filter traceback entries from appearing to the user;

src/_pytest/_code/source.py

Lines changed: 15 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from bisect import bisect_right
99
from types import CodeType
1010
from types import FrameType
11-
from typing import Any
1211
from typing import Iterator
1312
from typing import List
1413
from typing import Optional
@@ -18,7 +17,6 @@
1817

1918
import py
2019

21-
from _pytest.compat import get_real_func
2220
from _pytest.compat import overload
2321
from _pytest.compat import TYPE_CHECKING
2422

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

281279

282-
def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]:
283-
""" Return source location (path, lineno) for the given object.
284-
If the source cannot be determined return ("", -1).
285-
286-
The line number is 0-based.
287-
"""
288-
from .code import Code
289-
290-
# xxx let decorators etc specify a sane ordering
291-
# NOTE: this used to be done in _pytest.compat.getfslineno, initially added
292-
# in 6ec13a2b9. It ("place_as") appears to be something very custom.
293-
obj = get_real_func(obj)
294-
if hasattr(obj, "place_as"):
295-
obj = obj.place_as
296-
297-
try:
298-
code = Code(obj)
299-
except TypeError:
300-
try:
301-
fn = inspect.getsourcefile(obj) or inspect.getfile(obj)
302-
except TypeError:
303-
return "", -1
304-
305-
fspath = fn and py.path.local(fn) or ""
306-
lineno = -1
307-
if fspath:
308-
try:
309-
_, lineno = findsource(obj)
310-
except OSError:
311-
pass
312-
return fspath, lineno
313-
else:
314-
return code.path, code.firstlineno
315-
316-
317280
#
318281
# helper functions
319282
#
@@ -329,9 +292,22 @@ def findsource(obj) -> Tuple[Optional[Source], int]:
329292
return source, lineno
330293

331294

332-
def getsource(obj, **kwargs) -> Source:
333-
from .code import getrawcode
295+
def getrawcode(obj, trycall: bool = True):
296+
""" return code object for given function. """
297+
try:
298+
return obj.__code__
299+
except AttributeError:
300+
obj = getattr(obj, "f_code", obj)
301+
obj = getattr(obj, "__code__", obj)
302+
if trycall and not hasattr(obj, "co_firstlineno"):
303+
if hasattr(obj, "__call__") and not inspect.isclass(obj):
304+
x = getrawcode(obj.__call__, trycall=False)
305+
if hasattr(x, "co_firstlineno"):
306+
return x
307+
return obj
334308

309+
310+
def getsource(obj, **kwargs) -> Source:
335311
obj = getrawcode(obj)
336312
try:
337313
strsrc = inspect.getsource(obj)
@@ -346,8 +322,6 @@ def deindent(lines: Sequence[str]) -> List[str]:
346322

347323

348324
def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[int]]:
349-
import ast
350-
351325
# flatten all statements and except handlers into one lineno-list
352326
# AST's line numbers start indexing at 1
353327
values = [] # type: List[int]

src/_pytest/fixtures.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
import py
1313

1414
import _pytest
15+
from _pytest._code import getfslineno
1516
from _pytest._code.code import FormattedExcinfo
1617
from _pytest._code.code import TerminalRepr
17-
from _pytest._code.source import getfslineno
1818
from _pytest._io import TerminalWriter
1919
from _pytest.compat import _format_args
2020
from _pytest.compat import _PytestWrapper

src/_pytest/mark/structures.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import attr
1616

17-
from .._code.source import getfslineno
17+
from .._code import getfslineno
1818
from ..compat import ascii_escaped
1919
from ..compat import NOTSET
2020
from _pytest.outcomes import fail

src/_pytest/nodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
import py
1313

1414
import _pytest._code
15+
from _pytest._code import getfslineno
1516
from _pytest._code.code import ExceptionChainRepr
1617
from _pytest._code.code import ExceptionInfo
1718
from _pytest._code.code import ReprExceptionInfo
18-
from _pytest._code.source import getfslineno
1919
from _pytest.compat import cached_property
2020
from _pytest.compat import TYPE_CHECKING
2121
from _pytest.config import Config

src/_pytest/python.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
from _pytest import fixtures
2626
from _pytest import nodes
2727
from _pytest._code import filter_traceback
28+
from _pytest._code import getfslineno
2829
from _pytest._code.code import ExceptionInfo
29-
from _pytest._code.source import getfslineno
3030
from _pytest._io import TerminalWriter
3131
from _pytest._io.saferepr import saferepr
3232
from _pytest.compat import ascii_escaped

0 commit comments

Comments
 (0)