Source code for src.utils

import logging
from collections.abc import Callable, Iterable
from pathlib import Path
from typing import Optional

from git.repo import Repo

logger: logging.Logger = logging.getLogger(__name__)


# shamelessly stolen from
# https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case/44969381#44969381
[docs] def camel_to_snake(s: str) -> str: """ Converts a string from CamelCase to snake_case. Does not handle many of the special cases. """ return "".join(["_" + c.lower() if c.isupper() else c for c in s]).lstrip( "_" )
[docs] def dir_list( repo: Repo, dir_predicate: Callable[[str], bool] ) -> Iterable[Path]: """ Searches the project for directories that match the `dir_predicate`. If a directory matches its subtree is not explored for more matching directories. :param repo: The local checkout of the project. :param dir_predicate: Function that receives the directory name and returns True iff the spanned subtree should be yielded. """ trees: Optional[list[Path]] = None try: logger.debug(f"Assuming fs repo base at {repo.working_dir}") trees = [Path(repo.working_dir)] except ValueError as E: logger.error(f"Unable to obtain tree for {repo}: {E}") while trees: tree: Path = trees.pop() for f in tree.iterdir(): if not f.is_dir(): continue if dir_predicate(f.name): yield f trees.append(f)
[docs] def file_list( repo: Repo, file_name_filter: Callable[[str], bool] = lambda _: False, path_component_filter: Callable[[str], bool] = lambda _: False, root: Optional[Path] = None, recursive: bool = True, ) -> Iterable[Path]: """ Generates a list of all files in a project. Optionally, the yielded files can be filtered based on path components or file name. The function can also be restricted to a subtree. :param repo: The local checkout of the project. :param file_name_filter: Function that receives a file name and returns True iff the file should be skipped. :param path_component_filter: Function that receives the name of a directory and returns True iff the whole directory should be skipped. :param root: Optionally, search only the subtree rooted at root :param recursive: Iterate through folders recursively :return: Iterator over all files in the project """ trees: Optional[list[Path]] = None try: logger.debug(f"Assuming fs repo base at {repo.working_dir}") trees = [root if root else Path(str(repo.working_dir))] except ValueError as E: logger.error(f"Unable to obtain tree for {repo}: {E}") while trees: tree: Path = trees.pop() for f in tree.iterdir(): if f.is_symlink(): continue if f.is_file() and file_name_filter(f.name): logger.debug(f"Skipping filtered file {f.as_posix()}") continue if f.is_dir() and recursive: if path_component_filter(f.name): logger.debug(f"Skipping filtered subtree {f.as_posix()}") else: trees.append(f) continue if not f.is_file(): continue logger.debug(f"Yielding {f.as_posix()}") yield f
[docs] def get_publiccode_cfg(repo: Repo) -> Path | None: """ Tries to find the OpenCoDE configuration file in the repository. :param repo: The local checkout of the project. :return: path of the file or None if it does not exist. """ files_in_repo_root = {f.name: f for f in file_list(repo, recursive=False)} for path in ("publiccode.yml", "publiccode.yaml"): if path in files_in_repo_root: return files_in_repo_root[path] return None