# -*- coding: utf-8 -*-"""Locate the test file path for a given Python source file.This utility calculates where a test file should be placed for a given Pythonsource file, following the Python test strategy naming convention: Source: git-repo/<package_name>/<subpackage>/<module>.py Test: tests/<subpackage>/test_<subpackage>_<module>.pyGiven an absolute path to a source file, this script:1. Finds the project root (by locating pyproject.toml)2. Determines the relative path from project root to source file3. Calculates the correct test file path using naming convention4. Prints the absolute test file pathThis is useful for:- IDE integrations that need to jump from source to test file- Build tools that generate test files in the correct location- Pre-commit hooks that validate tests exist for changed source files- Development workflows that automate test file creation"""frompathlibimportPathfrom..py_projectimportlocate_pyproject_toml
[docs]defcalculate_test_file_path(source_file_path:Path,project_root:Path,)->Path:""" Calculate the test file path for a given source file. Applies the naming convention: tests/<subpackage>/test_<subpackage>_<module>.py For example: Source: my_package/math/operations/calculator.py Test: tests/math/operations/test_math_operations_calculator.py :param source_file_path: Absolute path to the source file. :param project_root: Absolute path to the project root. :return: Absolute path where the test file should be located. :raises ValueError: If source file is not within the project. """# Get relative path from project roottry:relative_source=source_file_path.relative_to(project_root)exceptValueError:raiseValueError(f"Source file {source_file_path} is not within project root {project_root}")# Get all path parts: ['package_name', 'subpackage', 'module.py']parts=relative_source.partsskip_first=1# Skip the project/package name (first directory)# Extract subdirectory and module name# parts[1:-1] are subdirectories, parts[-1] is the module filenamesubdirs=parts[skip_first:-1]module_filename=parts[-1].replace(".py","")# Build test filenameifsubdirs:# Has subdirectories: test_<subdir1>_<subdir2>_<module>.pytest_filename=f"test_{'_'.join(subdirs)}_{module_filename}.py"test_dir=project_root/"tests"/Path(*subdirs)else:# Root level: test_<module>.pytest_filename=f"test_{module_filename}.py"test_dir=project_root/"tests"returntest_dir/test_filename
[docs]defmain(source_file:str)->str:""" Get the test file path for a Python source file. Maps source to test by stripping package name and joining path parts: ``my_pkg/a/b/c.py`` -> ``tests/a/b/test_a_b_c.py`` Usage:: shai-py test-path --source-file /path/to/my_pkg/a/b/c.py :param source_file: Absolute path to the Python source file. """source_path=Path(source_file)# Find project rootpyproject=locate_pyproject_toml(source_path.parent)ifnotpyproject:raiseFileNotFoundError("Could not locate pyproject.toml (project root not found)")project_root=pyproject.parent# Calculate and print test file pathtest_path=calculate_test_file_path(source_path,project_root)print(test_path)returntest_path