CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-darker

Apply Black formatting only in regions changed since last commit

Pending
Overview
Eval results
Files

file-utilities.mddocs/

File and Path Utilities

Helper functions for working with files, directories, and path filtering. These utilities support project discovery, configuration file location, and file filtering based on include/exclude patterns.

Capabilities

Project Configuration Discovery

Functions for finding project configuration files and roots.

def find_pyproject_toml(path_search_start: tuple[str, ...]) -> str | None:
    """
    Find the absolute filepath to a pyproject.toml if it exists.
    
    Searches upward from the given starting paths to locate the project
    root and check for pyproject.toml configuration.
    
    Parameters:
    - path_search_start: Tuple of starting paths to search from
    
    Returns:
    Absolute path to pyproject.toml file, or None if not found
    """

File Filtering Constants

Pre-compiled regular expressions for filtering Python files and excluding common directories.

DEFAULT_EXCLUDE_RE: Pattern[str]
"""
Regular expression for default exclusion patterns.

Excludes common directories like:
- .git, .hg, .svn (version control)
- .mypy_cache, .pytest_cache, .ruff_cache (tool caches)  
- .tox, .nox (testing environments)
- __pycache__, .eggs (Python artifacts)
- build, dist, _build (build outputs)
- .venv, venv (virtual environments)
- .vscode, .direnv (editor/tool directories)
"""

DEFAULT_INCLUDE_RE: Pattern[str]
"""
Regular expression for default inclusion patterns.

Includes files with extensions:
- .py (Python files)
- .pyi (Python stub files)  
- .ipynb (Jupyter notebooks)
"""

Path Processing Functions

Functions for processing and filtering file paths.

def filter_python_files_directory(
    paths: Collection[Path],
    root: Path,
    include: Pattern[str],
    exclude: Pattern[str],
    extend_exclude: Pattern[str] | None,
    force_exclude: Pattern[str] | None,
    formatter: BaseFormatter,
) -> Iterator[Path]:
    """
    Filter Python files in directories based on include/exclude patterns.
    
    Parameters:
    - paths: Collection of file/directory paths to process
    - root: Root directory for relative path calculations
    - include: Pattern for files to include
    - exclude: Pattern for files/directories to exclude
    - extend_exclude: Additional exclusion pattern
    - force_exclude: Pattern that overrides all inclusion rules
    - formatter: Formatter instance for additional filtering
    
    Yields:
    Path objects for Python files that match the filtering criteria
    """

def gen_python_files(
    paths: Collection[Path],
    root: Path, 
    include: Pattern[str],
    exclude: Pattern[str],
    extend_exclude: Pattern[str] | None = None,
    force_exclude: Pattern[str] | None = None,
) -> Iterator[Path]:
    """
    Generate Python file paths from given directories and files.
    
    Parameters:
    - paths: Paths to files/directories to process
    - root: Project root directory
    - include: Pattern for files to include  
    - exclude: Pattern for files/directories to exclude
    - extend_exclude: Additional exclusion pattern
    - force_exclude: Pattern that overrides inclusion rules
    
    Yields:
    Path objects for Python files matching the criteria
    """

Usage Examples

Finding Project Configuration

from darker.files import find_pyproject_toml

# Find pyproject.toml starting from current directory
config_path = find_pyproject_toml((".",))

if config_path:
    print(f"Found project config: {config_path}")
    
    # Read configuration
    import tomllib
    with open(config_path, 'rb') as f:
        config = tomllib.load(f)
        
    darker_config = config.get('tool', {}).get('darker', {})
    print(f"Darker config: {darker_config}")
else:
    print("No pyproject.toml found")

Using Default Filter Patterns

from darker.files import DEFAULT_INCLUDE_RE, DEFAULT_EXCLUDE_RE
from pathlib import Path

# Test if files match default patterns
test_files = [
    "src/module.py",
    "tests/test_module.py", 
    "setup.py",
    "README.md",
    ".git/config",
    "__pycache__/module.pyc",
    "dist/package.whl"
]

for file_path in test_files:
    path = Path(file_path)
    
    # Check inclusion
    includes = DEFAULT_INCLUDE_RE.search(str(path))
    
    # Check exclusion  
    excludes = DEFAULT_EXCLUDE_RE.search(str(path))
    
    if includes and not excludes:
        print(f"✓ Would process: {file_path}")
    else:
        reason = "excluded" if excludes else "not Python file"
        print(f"✗ Would skip: {file_path} ({reason})")

Custom File Filtering

import re
from darker.files import gen_python_files
from pathlib import Path

# Create custom patterns
custom_include = re.compile(r'(\.py|\.pyx)$')  # Include Cython files
custom_exclude = re.compile(r'/(build|dist|\.tox)/')
custom_extend_exclude = re.compile(r'/legacy/')  # Skip legacy code

# Find Python files with custom filters
root = Path(".")
paths = [Path("src"), Path("tests")]

python_files = list(gen_python_files(
    paths=paths,
    root=root,
    include=custom_include,
    exclude=custom_exclude,
    extend_exclude=custom_extend_exclude
))

print(f"Found {len(python_files)} Python files:")
for file_path in python_files:
    print(f"  {file_path}")

Integration with Formatter Filtering

from darker.files import filter_python_files_directory
from darker.formatters import create_formatter
from pathlib import Path
import re

# Set up formatter and patterns
formatter = create_formatter("black")
root = Path(".")
paths = [Path("src"), Path("tests")]

include_pattern = re.compile(r'\.pyi?$')
exclude_pattern = re.compile(r'/(\.git|__pycache__|\.pytest_cache)/')

# Filter files that the formatter can process
filtered_files = list(filter_python_files_directory(
    paths=paths,
    root=root,
    include=include_pattern,
    exclude=exclude_pattern,
    extend_exclude=None,
    force_exclude=None,
    formatter=formatter
))

print(f"Files to format with {formatter.name}:")
for file_path in filtered_files:
    print(f"  {file_path}")

Recursive Directory Processing

from darker.files import gen_python_files, DEFAULT_INCLUDE_RE, DEFAULT_EXCLUDE_RE
from pathlib import Path

def find_all_python_files(start_path: Path) -> list[Path]:
    """Find all Python files recursively from a starting path."""
    
    if start_path.is_file():
        # Single file
        if DEFAULT_INCLUDE_RE.search(str(start_path)):
            return [start_path]
        else:
            return []
    
    # Directory - search recursively
    return list(gen_python_files(
        paths=[start_path],
        root=start_path,
        include=DEFAULT_INCLUDE_RE,
        exclude=DEFAULT_EXCLUDE_RE
    ))

# Usage examples
project_files = find_all_python_files(Path("src"))
test_files = find_all_python_files(Path("tests"))
single_file = find_all_python_files(Path("setup.py"))

print(f"Project files: {len(project_files)}")
print(f"Test files: {len(test_files)}")  
print(f"Single file: {len(single_file)}")

Install with Tessl CLI

npx tessl i tessl/pypi-darker@3.0.1

docs

chooser.md

command-line.md

configuration.md

diff-utilities.md

file-utilities.md

formatters.md

git-integration.md

index.md

main-functions.md

preprocessors.md

verification.md

tile.json