legacypath.py
Overview
The [legacypath.py](/projects/286/67505) module provides backward compatibility support for the legacy `py.path.local` path type within the `pytest` testing framework ecosystem. It wraps modern `pathlib.Path` based implementations and exposes legacy-style path objects (`LEGACY_PATH`) where needed, ensuring older plugins and test code that rely on the legacy path API continue to work without modification.
This compatibility layer is essential during the transition from legacy `py.path` to `pathlib.Path` in `pytest`. It offers legacy wrappers for core test directory management, temporary directory factories, and common pytest components (e.g., `Config`, `Session`, `Node`) by monkeypatching properties and methods to return `LEGACY_PATH` types.
The file mainly defines:
A
Testdirclass that wraps the modernPytestertest directory abstraction to provide legacy path objects.A
TempdirFactorydataclass that wrapsTempPathFactoryfor legacy path support.Plugins to provide fixtures returning legacy path objects.
Monkeypatches to add legacy path properties to key pytest classes.
Hooks that integrate these monkeypatches into pytest lifecycle events.
Detailed API Documentation
Constants & Types
LEGACY_PATH: A type alias imported from_pytest.compat, representing the legacypy.path.localpath type.legacy_path(path): A function (imported) that converts modernpathlib.Pathor similar paths to the legacyLEGACY_PATHwrapper.
Class: Testdir
A backward compatibility wrapper around the modern `Pytester` class, adapting all method calls and path-related properties to return legacy `LEGACY_PATH` objects.
Purpose
To provide a legacy-compatible test directory helper for tests or plugins that expect the old `py.path.local` style API.
Attributes
_pytester: Pytester– The internal modernPytesterinstance to which calls are forwarded.CLOSE_STDIN: Final – Constant copied from
Pytesterfor subprocess stdin control.TimeoutExpired: Final – Exception type from
Pytesterfor subprocess timeouts.
Properties
tmpdir -> LEGACY_PATH
Temporary directory path as legacy path where test files are created.test_tmproot -> LEGACY_PATH
Root temporary directory.request
The underlyingFixtureRequestinstance.plugins
Get/set the plugin manager.monkeypatch -> MonkeyPatch
The monkeypatching helper object.
Methods
All methods forward to the internal `Pytester` instance, converting paths to legacy paths as needed.
makefile(ext, *args, **kwargs) -> LEGACY_PATH
Make a file with the given extension and content.makeconftest(source) -> LEGACY_PATH
Create a conftest.py file with given source.makeini(source) -> LEGACY_PATH
Create a pytest.ini file from source.getinicfg(source: str) -> SectionWrapper
Parse ini configuration from source.makepyprojecttoml(source) -> LEGACY_PATH
Create a pyproject.toml file.makepyfile(*args, **kwargs) -> LEGACY_PATH
Create a Python test file.maketxtfile(*args, **kwargs) -> LEGACY_PATH
Create a text file.syspathinsert(path=None) -> None
Insert a path intosys.path.mkdir(name) -> LEGACY_PATH
Create a directory.mkpydir(name) -> LEGACY_PATH
Create a Python package directory.copy_example(name=None) -> LEGACY_PATH
Copy an example test directory.getnode(config: Config, arg) -> Item | Collector | None
Get a pytest node.getpathnode(path)
Get a node for a given path.genitems(colitems: list[Item | Collector]) -> list[Item]
Generate test items.runitem(source)
Run a test item.inline_runsource(source, *cmdlineargs)
Run source code inline.inline_genitems(*args)
Inline generate items.inline_run(*args, plugins=(), no_reraise_ctrlc=False)
Inline run with plugins.runpytest_inprocess(*args, **kwargs) -> RunResult
Run pytest in-process.runpytest(*args, **kwargs) -> RunResult
Run pytest normally.parseconfig(*args) -> Config
Parse pytest config.parseconfigure(*args) -> Config
Parse and configure pytest.getitem(source, funcname="test_func")
Get a test item by function name.getitems(source)
Get all test items from source.getmodulecol(source, configargs=(), withinit=False)
Get module collector.collect_by_name(modcol: Collector, name: str) -> Item | Collector | None
Collect test node by name.popen(cmdargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=CLOSE_STDIN, **kw)
Open a subprocess.run(*cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult
Run a command.runpython(script) -> RunResult
Run Python script.runpython_c(command)
Run Python command.runpytest_subprocess(*args, timeout=None) -> RunResult
Run pytest in a subprocess.spawn_pytest(string: str, expect_timeout: float = 10.0) -> pexpect.spawn
Spawn a pytest process.spawn(cmd: str, expect_timeout: float = 10.0) -> pexpect.spawn
Spawn a generic process.
Usage Example
def test_example(testdir):
# testdir is an instance of Testdir, returns legacy paths
test_file = testdir.makepyfile("def test_foo(): assert True")
result = testdir.runpytest(test_file)
assert result.ret == 0
Class: LegacyTestdirPlugin
A pytest plugin that provides a `testdir` fixture, which returns a `Testdir` instance wrapping a `Pytester`. This fixture is intended for backward compatibility.
testdir(pytester: Pytester) -> TestdirReturns a legacy path supporting testdir instance.
**Note:** New tests should prefer the `pytester` fixture instead.
Class: TempdirFactory
A backward compatibility dataclass wrapping `TempPathFactory` to provide legacy `py.path.local` style temporary directories.
Attributes
_tmppath_factory: TempPathFactory– the modern temporary path factory
Methods
mktemp(basename: str, numbered: bool = True) -> LEGACY_PATH
Create a temporary directory with the given basename, returning legacy path.getbasetemp() -> LEGACY_PATH
Return the base temporary directory as a legacy path.
Usage Example
def test_tempdir_factory(tmpdir_factory):
tempdir = tmpdir_factory.mktemp("data")
assert tempdir.check(dir=1) # legacy py.path method
Class: LegacyTmpdirPlugin
A pytest plugin providing legacy fixtures:
tmpdir_factory(request: FixtureRequest) -> TempdirFactory
Session-scoped fixture returning a legacyTempdirFactory.tmpdir(tmp_path: Path) -> LEGACY_PATH
Function-scoped fixture returning a legacy temporary directory.
Monkeypatched Methods / Properties
The module monkeypatches several pytest core classes to add legacy path properties or methods:
Cache.makedir(name: str) -> LEGACY_PATH
Create a directory and return legacy path.FixtureRequest.fspath -> LEGACY_PATH(deprecated)
Legacy path to the test module.TerminalReporter.startdir -> LEGACY_PATH
Directory from which pytest was invoked.Config.invocation_dir -> LEGACY_PATH
Directory from which pytest was invoked.Config.rootdir -> LEGACY_PATH
Path to rootdir.Config.inifile -> Optional[LEGACY_PATH]
Path to config file.Session.startdir -> LEGACY_PATH
Directory from which pytest was invoked.Config._getini_unknown_type(name: str, type: str, value: str | list[str])
Supports legacypathlistini type returning legacy paths.Node.fspath -> LEGACY_PATH(property with setter)
Legacy path for node path.
Pytest Hooks
pytest_load_initial_conftests(early_config: Config) -> None
Monkeypatches legacy attributes onto core pytest classes as early as possible.pytest_configure(config: Config) -> None
If thetmpdirplugin is active, installs theLegacyTmpdirPluginand sets up_tmpdirhandleron the config.pytest_plugin_registered(plugin: object, manager: PytestPluginManager) -> None
On registration of thepytesterplugin, registers theLegacyTestdirPluginfor legacy testdir fixture support.
Implementation Details & Algorithms
Wrapper pattern:
TestdirandTempdirFactorywrap modernpytestabstractions (Pytester,TempPathFactory) and convertpathlib.Pathinstances to legacypy.path.localobjects usinglegacy_path().Monkeypatching: Early in pytest startup (
pytest_load_initial_conftestshook), core classes are dynamically patched to add legacy properties and methods. This is done usingMonkeyPatchto avoid permanent modification and to allow clean undoing.Configuration parsing: Adds support for legacy
pathlistini option type by parsing strings withshlex.splitand converting to legacy paths.Plugin registration: Ensures legacy compatibility plugins are registered conditionally when relevant plugins (
tmpdir,pytester) are active.
Interaction with Other Parts of the System
pytesterplugin: The modern replacement fortestdir, wrapped byTestdirfor legacy support.Core pytest classes:
Config,Session,Node,Cache,FixtureRequest,TerminalReporter, etc., receive monkeypatched legacy properties.Temporary path management: Wraps
TempPathFactoryto provide legacy path APIs.Fixtures: Provides legacy fixtures
testdir,tmpdir,tmpdir_factorythat return legacy paths for backward compatibility.pytest plugin manager: Hooks into plugin registration lifecycle to install legacy support plugins.
Mermaid Diagram
classDiagram
class Testdir {
-_pytester: Pytester
+tmpdir: LEGACY_PATH
+test_tmproot: LEGACY_PATH
+request
+plugins
+monkeypatch: MonkeyPatch
+make_hook_recorder(pluginmanager) HookRecorder
+chdir() void
+finalize() void
+makefile(ext, *args, **kwargs) LEGACY_PATH
+makeconftest(source) LEGACY_PATH
+makeini(source) LEGACY_PATH
+getinicfg(source) SectionWrapper
+makepyprojecttoml(source) LEGACY_PATH
+makepyfile(*args, **kwargs) LEGACY_PATH
+maketxtfile(*args, **kwargs) LEGACY_PATH
+syspathinsert(path=None) void
+mkdir(name) LEGACY_PATH
+mkpydir(name) LEGACY_PATH
+copy_example(name=None) LEGACY_PATH
+getnode(config, arg) Item|Collector|None
+getpathnode(path)
+genitems(colitems) list~Item~
+runitem(source)
+inline_runsource(source, *cmdlineargs)
+inline_genitems(*args)
+inline_run(*args, plugins=(), no_reraise_ctrlc=False)
+runpytest_inprocess(*args, **kwargs) RunResult
+runpytest(*args, **kwargs) RunResult
+parseconfig(*args) Config
+parseconfigure(*args) Config
+getitem(source, funcname="test_func")
+getitems(source)
+getmodulecol(source, configargs=(), withinit=False)
+collect_by_name(modcol, name) Item|Collector|None
+popen(cmdargs, stdout, stderr, stdin, **kw)
+run(*cmdargs, timeout=None, stdin)
+runpython(script) RunResult
+runpython_c(command)
+runpytest_subprocess(*args, timeout=None) RunResult
+spawn_pytest(string, expect_timeout=10.0)
+spawn(cmd, expect_timeout=10.0)
}
class TempdirFactory {
-_tmppath_factory: TempPathFactory
+mktemp(basename, numbered=True) LEGACY_PATH
+getbasetemp() LEGACY_PATH
}
class LegacyTestdirPlugin {
+testdir(pytester) Testdir
}
class LegacyTmpdirPlugin {
+tmpdir_factory(request) TempdirFactory
+tmpdir(tmp_path) LEGACY_PATH
}
Testdir --> Pytester : wraps
TempdirFactory --> TempPathFactory : wraps
LegacyTestdirPlugin ..> Testdir : provides fixture
LegacyTmpdirPlugin ..> TempdirFactory : provides fixture
LegacyTmpdirPlugin ..> LEGACY_PATH : provides fixture
Summary
[legacypath.py](/projects/286/67505) is a critical backward compatibility module in `pytest` that ensures legacy `py.path.local` based plugins and tests continue to function seamlessly while the internal implementations move towards `pathlib.Path`. It achieves this via wrapper classes, monkeypatching core classes, and providing legacy fixtures. It is designed as a temporary bridge to ease the migration towards modern Python path handling within the pytest ecosystem.