Skip to content

Commit 2178ee8

Browse files
authored
Merge pull request #11826 from bluetech/no-cwd
Prefer using the invocation dir over CWD
2 parents eefc9d4 + a6dd90a commit 2178ee8

File tree

7 files changed

+133
-59
lines changed

7 files changed

+133
-59
lines changed

src/_pytest/config/__init__.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,7 @@ def _set_initial_conftests(
541541
noconftest: bool,
542542
rootpath: Path,
543543
confcutdir: Optional[Path],
544+
invocation_dir: Path,
544545
importmode: Union[ImportMode, str],
545546
) -> None:
546547
"""Load initial conftest files given a preparsed "namespace".
@@ -550,8 +551,9 @@ def _set_initial_conftests(
550551
All builtin and 3rd party plugins will have been loaded, however, so
551552
common options will not confuse our logic here.
552553
"""
553-
current = Path.cwd()
554-
self._confcutdir = absolutepath(current / confcutdir) if confcutdir else None
554+
self._confcutdir = (
555+
absolutepath(invocation_dir / confcutdir) if confcutdir else None
556+
)
555557
self._noconftest = noconftest
556558
self._using_pyargs = pyargs
557559
foundanchor = False
@@ -561,15 +563,15 @@ def _set_initial_conftests(
561563
i = path.find("::")
562564
if i != -1:
563565
path = path[:i]
564-
anchor = absolutepath(current / path)
566+
anchor = absolutepath(invocation_dir / path)
565567

566568
# Ensure we do not break if what appears to be an anchor
567569
# is in fact a very long option (#10169, #11394).
568570
if safe_exists(anchor):
569571
self._try_load_conftest(anchor, importmode, rootpath)
570572
foundanchor = True
571573
if not foundanchor:
572-
self._try_load_conftest(current, importmode, rootpath)
574+
self._try_load_conftest(invocation_dir, importmode, rootpath)
573575

574576
def _is_in_confcutdir(self, path: Path) -> bool:
575577
"""Whether a path is within the confcutdir.
@@ -1168,6 +1170,7 @@ def pytest_load_initial_conftests(self, early_config: "Config") -> None:
11681170
noconftest=early_config.known_args_namespace.noconftest,
11691171
rootpath=early_config.rootpath,
11701172
confcutdir=early_config.known_args_namespace.confcutdir,
1173+
invocation_dir=early_config.invocation_params.dir,
11711174
importmode=early_config.known_args_namespace.importmode,
11721175
)
11731176

@@ -1176,8 +1179,8 @@ def _initini(self, args: Sequence[str]) -> None:
11761179
args, namespace=copy.copy(self.option)
11771180
)
11781181
rootpath, inipath, inicfg = determine_setup(
1179-
ns.inifilename,
1180-
ns.file_or_dir + unknown_args,
1182+
inifile=ns.inifilename,
1183+
args=ns.file_or_dir + unknown_args,
11811184
rootdir_cmd_arg=ns.rootdir or None,
11821185
invocation_dir=self.invocation_params.dir,
11831186
)
@@ -1261,6 +1264,8 @@ def _decide_args(
12611264
"""Decide the args (initial paths/nodeids) to use given the relevant inputs.
12621265
12631266
:param warn: Whether can issue warnings.
1267+
1268+
:returns: The args and the args source. Guaranteed to be non-empty.
12641269
"""
12651270
if args:
12661271
source = Config.ArgsSource.ARGS

src/_pytest/config/findpaths.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def make_scalar(v: object) -> Union[str, List[str]]:
8787

8888

8989
def locate_config(
90+
invocation_dir: Path,
9091
args: Iterable[Path],
9192
) -> Tuple[Optional[Path], Optional[Path], Dict[str, Union[str, List[str]]]]:
9293
"""Search in the list of arguments for a valid ini-file for pytest,
@@ -100,7 +101,7 @@ def locate_config(
100101
]
101102
args = [x for x in args if not str(x).startswith("-")]
102103
if not args:
103-
args = [Path.cwd()]
104+
args = [invocation_dir]
104105
for arg in args:
105106
argpath = absolutepath(arg)
106107
for base in (argpath, *argpath.parents):
@@ -113,7 +114,10 @@ def locate_config(
113114
return None, None, {}
114115

115116

116-
def get_common_ancestor(paths: Iterable[Path]) -> Path:
117+
def get_common_ancestor(
118+
invocation_dir: Path,
119+
paths: Iterable[Path],
120+
) -> Path:
117121
common_ancestor: Optional[Path] = None
118122
for path in paths:
119123
if not path.exists():
@@ -130,7 +134,7 @@ def get_common_ancestor(paths: Iterable[Path]) -> Path:
130134
if shared is not None:
131135
common_ancestor = shared
132136
if common_ancestor is None:
133-
common_ancestor = Path.cwd()
137+
common_ancestor = invocation_dir
134138
elif common_ancestor.is_file():
135139
common_ancestor = common_ancestor.parent
136140
return common_ancestor
@@ -162,10 +166,11 @@ def get_dir_from_path(path: Path) -> Path:
162166

163167

164168
def determine_setup(
169+
*,
165170
inifile: Optional[str],
166171
args: Sequence[str],
167-
rootdir_cmd_arg: Optional[str] = None,
168-
invocation_dir: Optional[Path] = None,
172+
rootdir_cmd_arg: Optional[str],
173+
invocation_dir: Path,
169174
) -> Tuple[Path, Optional[Path], Dict[str, Union[str, List[str]]]]:
170175
"""Determine the rootdir, inifile and ini configuration values from the
171176
command line arguments.
@@ -177,8 +182,7 @@ def determine_setup(
177182
:param rootdir_cmd_arg:
178183
The `--rootdir` command line argument, if given.
179184
:param invocation_dir:
180-
The working directory when pytest was invoked, if known.
181-
If not known, the current working directory is used.
185+
The working directory when pytest was invoked.
182186
"""
183187
rootdir = None
184188
dirs = get_dirs_from_args(args)
@@ -189,22 +193,20 @@ def determine_setup(
189193
if rootdir_cmd_arg is None:
190194
rootdir = inipath_.parent
191195
else:
192-
ancestor = get_common_ancestor(dirs)
193-
rootdir, inipath, inicfg = locate_config([ancestor])
196+
ancestor = get_common_ancestor(invocation_dir, dirs)
197+
rootdir, inipath, inicfg = locate_config(invocation_dir, [ancestor])
194198
if rootdir is None and rootdir_cmd_arg is None:
195199
for possible_rootdir in (ancestor, *ancestor.parents):
196200
if (possible_rootdir / "setup.py").is_file():
197201
rootdir = possible_rootdir
198202
break
199203
else:
200204
if dirs != [ancestor]:
201-
rootdir, inipath, inicfg = locate_config(dirs)
205+
rootdir, inipath, inicfg = locate_config(invocation_dir, dirs)
202206
if rootdir is None:
203-
if invocation_dir is not None:
204-
cwd = invocation_dir
205-
else:
206-
cwd = Path.cwd()
207-
rootdir = get_common_ancestor([cwd, ancestor])
207+
rootdir = get_common_ancestor(
208+
invocation_dir, [invocation_dir, ancestor]
209+
)
208210
if is_fs_root(rootdir):
209211
rootdir = ancestor
210212
if rootdir_cmd_arg:

src/_pytest/helpconfig.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,11 @@ def pytest_cmdline_parse() -> Generator[None, Config, Config]:
109109
debugfile = open(path, "w", encoding="utf-8")
110110
debugfile.write(
111111
"versions pytest-%s, "
112-
"python-%s\ncwd=%s\nargs=%s\n\n"
112+
"python-%s\ninvocation_dir=%s\ncwd=%s\nargs=%s\n\n"
113113
% (
114114
pytest.__version__,
115115
".".join(map(str, sys.version_info)),
116+
config.invocation_params.dir,
116117
os.getcwd(),
117118
config.invocation_params.args,
118119
)

src/_pytest/python.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,14 +1525,13 @@ def _ascii_escaped_by_config(val: Union[str, bytes], config: Optional[Config]) -
15251525
return val if escape_option else ascii_escaped(val) # type: ignore
15261526

15271527

1528-
def _pretty_fixture_path(func) -> str:
1529-
cwd = Path.cwd()
1530-
loc = Path(getlocation(func, str(cwd)))
1528+
def _pretty_fixture_path(invocation_dir: Path, func) -> str:
1529+
loc = Path(getlocation(func, invocation_dir))
15311530
prefix = Path("...", "_pytest")
15321531
try:
15331532
return str(prefix / loc.relative_to(_PYTEST_DIR))
15341533
except ValueError:
1535-
return bestrelpath(cwd, loc)
1534+
return bestrelpath(invocation_dir, loc)
15361535

15371536

15381537
def show_fixtures_per_test(config):
@@ -1545,19 +1544,19 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None:
15451544
import _pytest.config
15461545

15471546
session.perform_collect()
1548-
curdir = Path.cwd()
1547+
invocation_dir = config.invocation_params.dir
15491548
tw = _pytest.config.create_terminal_writer(config)
15501549
verbose = config.getvalue("verbose")
15511550

15521551
def get_best_relpath(func) -> str:
1553-
loc = getlocation(func, str(curdir))
1554-
return bestrelpath(curdir, Path(loc))
1552+
loc = getlocation(func, invocation_dir)
1553+
return bestrelpath(invocation_dir, Path(loc))
15551554

15561555
def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None:
15571556
argname = fixture_def.argname
15581557
if verbose <= 0 and argname.startswith("_"):
15591558
return
1560-
prettypath = _pretty_fixture_path(fixture_def.func)
1559+
prettypath = _pretty_fixture_path(invocation_dir, fixture_def.func)
15611560
tw.write(f"{argname}", green=True)
15621561
tw.write(f" -- {prettypath}", yellow=True)
15631562
tw.write("\n")
@@ -1601,7 +1600,7 @@ def _showfixtures_main(config: Config, session: Session) -> None:
16011600
import _pytest.config
16021601

16031602
session.perform_collect()
1604-
curdir = Path.cwd()
1603+
invocation_dir = config.invocation_params.dir
16051604
tw = _pytest.config.create_terminal_writer(config)
16061605
verbose = config.getvalue("verbose")
16071606

@@ -1615,15 +1614,15 @@ def _showfixtures_main(config: Config, session: Session) -> None:
16151614
if not fixturedefs:
16161615
continue
16171616
for fixturedef in fixturedefs:
1618-
loc = getlocation(fixturedef.func, str(curdir))
1617+
loc = getlocation(fixturedef.func, invocation_dir)
16191618
if (fixturedef.argname, loc) in seen:
16201619
continue
16211620
seen.add((fixturedef.argname, loc))
16221621
available.append(
16231622
(
16241623
len(fixturedef.baseid),
16251624
fixturedef.func.__module__,
1626-
_pretty_fixture_path(fixturedef.func),
1625+
_pretty_fixture_path(invocation_dir, fixturedef.func),
16271626
fixturedef.argname,
16281627
fixturedef,
16291628
)

0 commit comments

Comments
 (0)