1"""
2Find directories
3"""
4import importlib
5import os
6import sys
7from typing import Any
8from typing import Dict
9from typing import Iterable
10from typing import List
11
12import dict_tools.update
13
14
15def dir_list(
16    subname: str, p_name: str, pypath: List[str] = None, static: List[str] = None
17) -> List[str]:
18    """
19    Return the directories to look for modules in, pypath specifies files
20    relative to an installed python package, static is for static dirs
21    :param subname: ignored
22    :param p_name: ignored
23    :param pypath: One or many python paths which will be imported
24    :param static: Directories that can be explicitly passed
25    """
26    ret = []
27    for path in pypath:
28        mod = importlib.import_module(path)
29        for m_path in mod.__path__:
30            # If we are inside of an executable the path will be different
31            ret.append(m_path)
32    ret.extend(static)
33    return ret
34
35
36def inline_dirs(dirs: Iterable[str], subdir: str) -> List[str]:
37    """
38    Look for the named subdir in the list of dirs
39    :param dirs: The names of configured dynamic dirs
40    :param subdir: The name of the subdir to check for in the list of dynamic dirs
41    :return An extended list of dirs that includes the found subdirs
42    """
43    ret = []
44    for dir_ in dirs:
45        check = os.path.join(dir_, subdir)
46        if os.path.isdir(check):
47            ret.append(check)
48    return ret
49
50
51def dynamic_dirs() -> Dict[str, Any]:
52    """
53    Iterate over the available python package imports and look for configured
54    dynamic dirs
55    """
56    dirs = []
57    ret = {}
58    for dir_ in sys.path:
59        if not os.path.isdir(dir_):
60            continue
61        for sub in os.listdir(dir_):
62            full = os.path.join(dir_, sub)
63            if full.endswith(".egg-link"):
64                with open(full) as rfh:
65                    dirs.append(rfh.read().strip())
66            if os.path.isdir(full):
67                dirs.append(full)
68    for dir_ in dirs:
69        conf = os.path.join(dir_, "conf.py")
70        context = {}
71        if not os.path.isfile(conf):
72            continue
73        try:
74            with open(conf) as f:
75                code = f.read()
76                if "DYNE" in code:
77                    exec(code, context)
78                else:
79                    continue
80        except Exception:
81            continue
82        if "DYNE" in context:
83            if not isinstance(context["DYNE"], dict):
84                continue
85            for name, paths in context["DYNE"].items():
86                if not isinstance(paths, list):
87                    continue
88                if name not in ret:
89                    ret[name] = {
90                        "paths": [],
91                        "CONFIG": {},
92                        "CLI_CONFIG": {},
93                        "SUBCOMMANDS": {},
94                    }
95                if "CONFIG" in context:
96                    dict_tools.update.update(ret[name]["CONFIG"], context["CONFIG"])
97                if "CLI_CONFIG" in context:
98                    dict_tools.update.update(
99                        ret[name]["CLI_CONFIG"], context["CLI_CONFIG"]
100                    )
101                if "SUBCOMMANDS" in context:
102                    dict_tools.update.update(
103                        ret[name]["SUBCOMMANDS"], context["SUBCOMMANDS"]
104                    )
105                for path in paths:
106                    ref = os.path.join(dir_, path.replace(".", os.sep))
107                    if dir_.endswith(name):
108                        ret[name]["paths"].insert(0, ref)
109                    else:
110                        ret[name]["paths"].append(ref)
111    for name in ret:
112        if not ret[name]:
113            continue
114        first = ret[name]["paths"].pop(0)
115        ret[name]["paths"] = sorted(ret[name]["paths"])
116        ret[name]["paths"].insert(0, first)
117    return ret
118