diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index 1c69498ea..0bc2e243e 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -25,13 +25,14 @@ if TYPE_CHECKING: class Source: - """ an immutable object holding a source code fragment, - possibly deindenting it. + """An immutable object holding a source code fragment. + + When using Source(...), the source lines are deindented. """ _compilecounter = 0 - def __init__(self, *parts, deindent: bool = True) -> None: + def __init__(self, *parts) -> None: self.lines = lines = [] # type: List[str] for part in parts: if not part: @@ -43,9 +44,8 @@ class Source: elif isinstance(part, str): partlines = part.split("\n") else: - partlines = getsource(part, deindent=deindent).lines - if deindent: - partlines = _deindent_function(partlines) + partlines = getsource(part).lines + partlines = deindent(partlines) lines.extend(partlines) def __eq__(self, other): @@ -306,24 +306,20 @@ def getrawcode(obj, trycall: bool = True): return obj -def getsource(obj, *, deindent: bool = True) -> Source: +def getsource(obj) -> Source: obj = getrawcode(obj) try: strsrc = inspect.getsource(obj) except IndentationError: strsrc = '"Buggy python version consider upgrading, cannot get source"' assert isinstance(strsrc, str) - return Source(strsrc, deindent=deindent) + return Source(strsrc) def deindent(lines: Sequence[str]) -> List[str]: return textwrap.dedent("\n".join(lines)).splitlines() -# Internal alias to avoid shadowing with `deindent` parameter. -_deindent_function = deindent - - def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[int]]: # flatten all statements and except handlers into one lineno-list # AST's line numbers start indexing at 1 diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 35728c334..014034dec 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -4,6 +4,7 @@ import ast import inspect import sys +import textwrap from types import CodeType from typing import Any from typing import Dict @@ -64,8 +65,6 @@ def test_source_from_inner_function() -> None: def f(): pass - source = _pytest._code.Source(f, deindent=False) - assert str(source).startswith(" def f():") source = _pytest._code.Source(f) assert str(source).startswith("def f():") @@ -557,7 +556,7 @@ def test_code_of_object_instance_with_call() -> None: def getstatement(lineno: int, source) -> Source: from _pytest._code.source import getstatementrange_ast - src = _pytest._code.Source(source, deindent=False) + src = _pytest._code.Source(source) ast, start, end = getstatementrange_ast(lineno, src) return src[start:end] @@ -633,7 +632,7 @@ def test_source_with_decorator() -> None: assert False src = inspect.getsource(deco_mark) - assert str(Source(deco_mark, deindent=False)) == src + assert textwrap.indent(str(Source(deco_mark)), " ") + "\n" == src assert src.startswith(" @pytest.mark.foo") @pytest.fixture @@ -646,7 +645,9 @@ def test_source_with_decorator() -> None: # existing behavior here for explicitness, but perhaps we should revisit/change this # in the future assert str(Source(deco_fixture)).startswith("@functools.wraps(function)") - assert str(Source(get_real_func(deco_fixture), deindent=False)) == src + assert ( + textwrap.indent(str(Source(get_real_func(deco_fixture))), " ") + "\n" == src + ) def test_single_line_else() -> None: