1+ """
2+ Helper script for generating the necessary RST files.
3+ """
4+
5+ import os
6+ import pathlib
7+
8+ from typing import Dict , List , NamedTuple , Tuple
9+
10+
11+ DIRECTORY = pathlib .Path (__file__ ).parent
12+
13+
14+ class Package (NamedTuple ):
15+ """
16+ A class representing one of the subpackages of a module.
17+ """
18+ modules : List [str ]
19+ packages : List [str ]
20+
21+
22+
23+ def _readProject (root ) -> Dict [str , Dict [str , bool ]]:
24+ """
25+ Searches a project for Python files, using their locations to create a
26+ dictionary of module paths and their submodules/subpackages. Submodules/subpackages will be a dictionary, where the key is the name and the
27+ """
28+ # This whole function could almost certainly be optimized, but I'll worry
29+ # about that some other time.
30+ root = pathlib .Path (root )
31+ rootName = root .name
32+ ret = {rootName : {}}
33+ for x in root .glob ('**/*.py' ):
34+ # Ignore internal files.
35+ if x .name .startswith ('_' ):
36+ continue
37+
38+ # Get all parent components.
39+ parents = []
40+ parent = x .parent
41+ while parent != root :
42+ parents .append (parent .name )
43+ parent = parent .parent
44+
45+ # Check if any of the parents start with an underscore. If they do,
46+ # ignore the current path.
47+ if any (y .startswith ('_' ) for y in parents ):
48+ continue
49+
50+ parents .append (rootName )
51+
52+ parents .reverse ()
53+
54+ # Add the subpackages and submodules.
55+ for index , name in enumerate (parents [1 :]):
56+ path = '.' .join (parents [:index + 1 ])
57+ if path not in ret :
58+ ret [path ] = {}
59+ if name not in ret [path ]:
60+ ret [path ][name ] = True
61+ if (path := '.' .join (parents )) not in ret :
62+ ret [path ] = {}
63+ ret [path ][x .name ] = False
64+
65+ return ret
66+
67+
68+ def _makePackage (name : str , data : Dict [str , bool ]) -> Package :
69+ return Package ([f'{ name } .{ x } ' for x in data if not data [x ]], [f'{ name } .{ x } ' for x in data if data [x ]])
70+
71+
72+ def run ():
73+ for x in getAutoGenerated ():
74+ os .remove (DIRECTORY / x )
75+ project = readProject (DIRECTORY .parent / 'extract_msg' )
76+ for x , y in project .items ():
77+ generateFile (x , y )
78+
79+ writeAutoGenerated ((x + '.rst' for x in project ))
80+
81+
82+ def generateFile (name : str , package : Package ):
83+ with open (DIRECTORY / (name + '.rst' ), 'w' ) as f :
84+ # Header.
85+ temp = name .replace ('_' , '\\ _' ) + ' package'
86+ f .write (f'{ temp } \n { "=" * len (temp )} \n \n ' )
87+
88+ # Subpackages.
89+ if package .packages :
90+ f .write ('Subpackages\n -----------\n \n ' )
91+ f .write ('.. toctree::\n ' )
92+ f .write (' :maxdepth: 4\n \n ' )
93+ f .write (' ' + '\n ' .join (package .packages ))
94+ f .write ('\n \n ' )
95+
96+ # Submodules.
97+ if package .modules :
98+ f .write ('Submodules\n ----------\n \n ' )
99+ for module in package .modules :
100+ temp = module .replace ('_' , '\\ _' ) + ' module'
101+ f .write (f'{ temp } \n { "-" * len (temp )} \n \n ' )
102+ f .write (f'.. automodule:: { module } \n ' )
103+ f .write (' :members:\n ' )
104+ f .write (' :undoc-members:\n ' )
105+ f .write (' :show-inheritance:\n \n ' )
106+
107+ # Module contents.
108+ f .write ('Module contents\n ---------------\n \n ' )
109+ f .write (f'.. automodule:: { name } \n ' )
110+ f .write (' :members:\n ' )
111+ f .write (' :undoc-members:\n ' )
112+ f .write (' :show-inheritance:\n ' )
113+
114+
115+ def getAutoGenerated () -> List [str ]:
116+ """
117+ Retrieves the list of previously autogenerated files.
118+ """
119+ with open (DIRECTORY / '_autogen.txt' , 'r' ) as f :
120+ return [x .strip () for x in f if x ]
121+
122+
123+ def readProject (root ) -> Dict [str , Package ]:
124+ """
125+ Returns a dictionary of package names to Package instances for a project.
126+ """
127+ initialRead = _readProject (root )
128+ return {x : _makePackage (x , y ) for x , y in initialRead .items ()}
129+
130+
131+ def writeAutoGenerated (files : List [str ]) -> None :
132+ """
133+ Writes the _autogen.txt file.
134+ """
135+ with open (DIRECTORY / '_autogen.txt' , 'w' ) as f :
136+ f .write ('\n ' .join (files ))
137+
138+
139+ if __name__ == '__main__' :
140+ run ()
0 commit comments