Source code for jfscripts.list_files

#! /usr/bin/env python3

from __future__ import annotations

import argparse
import fnmatch
import os
import re
from pathlib import Path
from typing import List, Tuple

from jfscripts import __version__


[docs] def is_glob(path_spec: str) -> bool: if re.search(r"[\*\?]", path_spec) or re.search(r"\[\!?.*\]", path_spec): return True else: return False
[docs] def common_path(paths: List[str]) -> str: common_path = os.path.commonpath(paths) if os.path.isdir(common_path): return common_path else: return str(Path(common_path).parent.resolve())
def _split_glob(glob_path: str) -> Tuple[str, str]: """Split a file path (e. g.: /data/(asterisk).txt) containing glob wildcard characters in a glob free path prefix (e. g.: /data) and a glob pattern (e. g. (asterisk).txt). :param glob_path: A file path containing glob wildcard characters. """ globs = glob_path.split(os.path.sep) no_globs: List[str] = [] for g in globs: if not is_glob(g): no_globs.append(g) else: break if not no_globs: dir_path = "." else: dir_path = os.path.sep.join(no_globs) return ( dir_path, os.path.sep.join(globs[len(no_globs) :]), ) def _list_files_all(dir_path: str) -> List[str]: output: List[str] = [] for root, dirs, files in os.walk(dir_path): for d in dirs: output.append(os.path.join(root, d)) for f in files: output.append(os.path.join(root, f)) output.sort() return output def _list_files_filter(dir_path: str, glob_pattern: str) -> List[str]: output: List[str] = [] for root, _, files in os.walk(dir_path): relroot = root[len(dir_path) :] for f in files: relfiles = os.path.join(relroot, f) if fnmatch.fnmatch(relfiles, glob_pattern): output.append(os.path.join(root, f)) output.sort() return output
[docs] def list_files(files: List[str], default_glob: str | None = None): """ :param list files: A list of file paths or a single element list containing a glob string. :param string default_glob: A default glob pattern like “(asterisk).txt”. This argument is only taken into account, if “element” is a list with only one entry and this entry is a path to a directory. """ if len(files) > 1: return files file_path = files[0] if not is_glob(file_path): if os.path.isdir(file_path): if default_glob: return _list_files_filter(file_path, default_glob) else: return _list_files_all(file_path) else: # not a directory return [file_path] else: # is glob glob_prefix, glob_pattern = _split_glob(file_path) return _list_files_filter(glob_prefix, glob_pattern) raise ValueError("Something went wrong.")
[docs] def doc_examples( command_name: str = "", extension: str = "txt", indent_spaces: int = 0, inline: bool = False, ): examples = ( "a.{}".format(extension), "a.{0} b.{0} c.{0}".format(extension), "(asterisk).{}".format(extension), '"(asterisk).{}"'.format(extension), "dir/", '"dir/(asterisk).{}"'.format(extension), ) if command_name or indent_spaces: prefix = "{}{} ".format(" " * indent_spaces, command_name) else: prefix = "" out: List[str] = [] for example in examples: command = "{}{}".format(prefix, example) if inline: command = "“{}”".format(command) out.append(command) if inline: join_phrase = ", " else: join_phrase = "\n" return join_phrase.join(out)
[docs] def get_parser(): """The argument parser for the command line interface. :return: A ArgumentParser object. :rtype: argparse.ArgumentParser """ parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description="This is a script to demonstrate the list_files() " "function in this file.\n\n" + doc_examples("list-files.py", "txt"), ) parser.add_argument( "input_files", help="Examples for this arguments are: " + doc_examples(inline=True), nargs="+", ) parser.add_argument( "-V", "--version", action="version", version="%(prog)s {version}".format(version=__version__), ) return parser
[docs] def main(): args = get_parser().parse_args() files = list_files(args.input_files) if files: for f in files: print(f) else: print("Nothing found to list. :-(")
if __name__ == "__main__": main()