/
niceSOFT
/
python3-pathspec
Обзор
Документация
Войти
/
niceSOFT
/
python3-pathspec
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
v1.0.0
tests/util.py
256 строк
7 KB
Caleb Burns
Fix test errors
25 дек 2025, 00:14
Не верифицирован
25 дек 2025, 00:14
075e241
Код
Авторство
О чём код?
""" This module provides utility functions shared by tests. """ from __future__ import annotations import itertools import os import os.path import pathlib from collections.abc import ( Iterable) from random import ( Random) from typing import ( Any, Optional, # Replaced by `X | None` in 3.10. cast) from unittest import ( SkipTest) from pathspec import ( PathSpec, RegexPattern) from pathspec.backend import ( BackendNamesHint) from pathspec._backends.hyperscan.base import ( hyperscan_error) from pathspec._backends.hyperscan._base import ( HyperscanExprDebug) from pathspec._backends.hyperscan.pathspec import ( HyperscanPsBackend) from pathspec._backends.re2.base import ( re2_error) from pathspec._backends.re2._base import ( Re2RegexDebug) from pathspec._backends.re2.pathspec import ( Re2PsBackend) from pathspec.util import ( CheckResult, TStrPath, TreeEntry) def debug_includes(spec: PathSpec, files: set[str], includes: set[str]) -> str: """ Format the match files message. *spec* (:class:`~pathspec.PathSpec`) is the path-spec. *files* (:class:`set` of :class:`str`) contains the source files. *includes* (:class:`set` of :class:`str`) contains the matched files. Returns the message (:class:`str`). """ results = [] for result in spec.check_files(files): assert (result.file in includes) == bool(result.include), { 'result': result, 'includes': includes, } results.append(result) return debug_results(spec, results) def debug_results(spec: PathSpec, results: Iterable[CheckResult[str]]) -> str: """ Format the check results message. *spec* (:class:`~pathspec.PathSpec`) is the path-spec. *results* (:class:`~collections.abc.Iterable` or :class:`~pathspec.util.CheckResult`) yields each file check result. Returns the message (:class:`str`). """ patterns = cast(list[RegexPattern], spec.patterns) pattern_table = [] if isinstance(spec._backend, HyperscanPsBackend) and spec._backend._debug_exprs: for expr_id, expr_dat in enumerate(spec._backend._expr_data, 1): assert isinstance(expr_dat, HyperscanExprDebug), expr_dat pattern = patterns[expr_dat.index] dir_col = 'd' if expr_dat.is_dir_pattern else '.' pattern_table.append(( f"{expr_dat.index+1}({expr_id}):{pattern.pattern}", f"{dir_col} {expr_dat.regex!r}", )) elif isinstance(spec._backend, Re2PsBackend) and spec._backend._debug_regex: for regex_id, regex_dat in enumerate(spec._backend._regex_data, 1): assert isinstance(regex_dat, Re2RegexDebug), regex_dat pattern = patterns[regex_dat.index] dir_col = 'd' if regex_dat.is_dir_pattern else '.' pattern_table.append(( f"{regex_dat.index+1}({regex_id}):{pattern.pattern}", f"{dir_col} {regex_dat.regex!r}", )) else: for index, pattern in enumerate(patterns, 1): pattern_table.append(( f"{index}:{pattern.pattern}", repr(pattern.regex.pattern), )) result_table = [] for result in results: if result.index is not None: pattern = patterns[result.index] result_table.append((f"{result.index + 1}:{pattern.pattern}", result.file)) else: result_table.append(("-", result.file)) result_table.sort(key=lambda r: r[1]) first_max_len = max(( len(__row[0]) for __row in itertools.chain(pattern_table, result_table) ), default=0) first_width = min(first_max_len, 20) pattern_lines = [] for row in pattern_table: pattern_lines.append(f" {row[0]:<{first_width}} {row[1]}") result_lines = [] for row in result_table: result_lines.append(f" {row[0]:<{first_width}} {row[1]}") return "\n".join([ "\n", " DEBUG ".center(32, "-"), *pattern_lines, "-"*32, *result_lines, "-"*32, ]) def get_includes(results: Iterable[CheckResult[TStrPath]]) -> set[TStrPath]: """ Get the included files from the check results. *results* (:class:`~collections.abc.Iterable` or :class:`~pathspec.util.CheckResult`) yields each file check result. Returns the included files (:class:`set` of :class:`str`). """ return {__res.file for __res in results if __res.include} def get_paths_from_entries(entries: Iterable[TreeEntry]) -> set[str]: """ Get the entry paths. *entries* (:class:`Iterable` of :class:`TreeEntry`) yields the entries. Returns the paths (:class:`set` of :class:`str`). """ return {__ent.path for __ent in entries} def make_dirs(temp_dir: pathlib.Path, dirs: Iterable[str]) -> None: """ Create the specified directories. *temp_dir* (:class:`pathlib.Path`) is the temporary directory to use. *dirs* (:class:`Iterable` of :class:`str`) is the POSIX directory paths (relative to *temp_dir*) to create. """ for dir in dirs: os.mkdir(temp_dir / ospath(dir)) def make_files(temp_dir: pathlib.Path, files: Iterable[str]) -> None: """ Create the specified files. *temp_dir* (:class:`pathlib.Path`) is the temporary directory to use. *files* (:class:`Iterable` of :class:`str`) is the POSIX file paths (relative to *temp_dir*) to create. """ for file in files: mkfile(temp_dir / ospath(file)) def make_links(temp_dir: pathlib.Path, links: Iterable[tuple[str, str]]) -> None: """ Create the specified links. *temp_dir* (:class:`pathlib.Path`) is the temporary directory to use. *links* (:class:`Iterable` of :class:`tuple`) contains the POSIX links to create relative to *temp_dir*. Each link (:class:`tuple`) contains the destination link path (:class:`str`) and source node path (:class:`str`). """ for link, node in links: src = temp_dir / ospath(node) dest = temp_dir / ospath(link) os.symlink(src, dest) def mkfile(file: pathlib.Path) -> None: """ Creates an empty file. *file* (:class:`pathlib.Path`) is the native file path to create. """ with open(file, 'wb'): pass def ospath(path: str) -> str: """ Convert the POSIX path to a native OS path. *path* (:class:`str`) is the POSIX path. Returns the native path (:class:`str`). """ return os.path.join(*path.split('/')) def require_backend(name: Optional[BackendNamesHint]) -> None: """ Skip the test if the backend library is not installed. *name* (:class:`str` or :data:`None`) is the backend name. Raises :class:`SkipTest` if the backend library is not installed. """ if name == 'hyperscan' and hyperscan_error is not None: raise SkipTest(str(hyperscan_error)) elif name == 're2' and re2_error is not None: raise SkipTest(str(re2_error)) def reverse_inplace(val: list[Any]) -> None: """ Reverse the list inplace. *val* (:class:`list`) is the list to sort. """ val.reverse() def shuffle_inplace(val: list[Any]) -> None: """ Shuffle the list inplace. The order will consistently be in the same random order between test runs. *val* (:class:`list`) is the list to sort. """ Random(0).shuffle(val)