File size: 4,833 Bytes
c61ccee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
from typing import Dict, List

from .glob_group import GlobGroup, GlobPattern

__all__ = ["Directory"]


class Directory:
    """A file structure representation. Organized as Directory nodes that have lists of

    their Directory children. Directories for a package are created by calling

    :meth:`PackageImporter.file_structure`."""

    def __init__(self, name: str, is_dir: bool):
        self.name = name
        self.is_dir = is_dir
        self.children: Dict[str, Directory] = {}

    def _get_dir(self, dirs: List[str]) -> "Directory":
        """Builds path of Directories if not yet built and returns last directory

        in list.



        Args:

            dirs (List[str]): List of directory names that are treated like a path.



        Returns:

            :class:`Directory`: The last Directory specified in the dirs list.

        """
        if len(dirs) == 0:
            return self
        dir_name = dirs[0]
        if dir_name not in self.children:
            self.children[dir_name] = Directory(dir_name, True)
        return self.children[dir_name]._get_dir(dirs[1:])

    def _add_file(self, file_path: str):
        """Adds a file to a Directory.



        Args:

            file_path (str): Path of file to add. Last element is added as a file while

                other paths items are added as directories.

        """
        *dirs, file = file_path.split("/")
        dir = self._get_dir(dirs)
        dir.children[file] = Directory(file, False)

    def has_file(self, filename: str) -> bool:
        """Checks if a file is present in a :class:`Directory`.



        Args:

            filename (str): Path of file to search for.

        Returns:

            bool: If a :class:`Directory` contains the specified file.

        """
        lineage = filename.split("/", maxsplit=1)
        child = lineage[0]
        grandchildren = lineage[1] if len(lineage) > 1 else None
        if child in self.children.keys():
            if grandchildren is None:
                return True
            else:
                return self.children[child].has_file(grandchildren)
        return False

    def __str__(self):
        str_list: List[str] = []
        self._stringify_tree(str_list)
        return "".join(str_list)

    def _stringify_tree(

        self, str_list: List[str], preamble: str = "", dir_ptr: str = "─── "

    ):
        """Recursive method to generate print-friendly version of a Directory."""
        space = "    "
        branch = "β”‚   "
        tee = "β”œβ”€β”€ "
        last = "└── "

        # add this directory's representation
        str_list.append(f"{preamble}{dir_ptr}{self.name}\n")

        # add directory's children representations
        if dir_ptr == tee:
            preamble = preamble + branch
        else:
            preamble = preamble + space

        file_keys: List[str] = []
        dir_keys: List[str] = []
        for key, val in self.children.items():
            if val.is_dir:
                dir_keys.append(key)
            else:
                file_keys.append(key)

        for index, key in enumerate(sorted(dir_keys)):
            if (index == len(dir_keys) - 1) and len(file_keys) == 0:
                self.children[key]._stringify_tree(str_list, preamble, last)
            else:
                self.children[key]._stringify_tree(str_list, preamble, tee)
        for index, file in enumerate(sorted(file_keys)):
            pointer = last if (index == len(file_keys) - 1) else tee
            str_list.append(f"{preamble}{pointer}{file}\n")


def _create_directory_from_file_list(

    filename: str,

    file_list: List[str],

    include: "GlobPattern" = "**",

    exclude: "GlobPattern" = (),

) -> Directory:
    """Return a :class:`Directory` file structure representation created from a list of files.



    Args:

        filename (str): The name given to the top-level directory that will be the

            relative root for all file paths found in the file_list.



        file_list (List[str]): List of files to add to the top-level directory.



        include (Union[List[str], str]): An optional pattern that limits what is included from the file_list to

            files whose name matches the pattern.



        exclude (Union[List[str], str]): An optional pattern that excludes files whose name match the pattern.



    Returns:

            :class:`Directory`: a :class:`Directory` file structure representation created from a list of files.

    """
    glob_pattern = GlobGroup(include, exclude=exclude, separator="/")

    top_dir = Directory(filename, True)
    for file in file_list:
        if glob_pattern.matches(file):
            top_dir._add_file(file)
    return top_dir