1+ from fnmatch import fnmatch
12from os .path import expanduser
23from pathlib import Path
3- from typing import Dict , Iterable , List , Optional , Sequence , TextIO , Tuple
4-
4+ from typing import (
5+ Callable ,
6+ Dict ,
7+ Iterable ,
8+ List ,
9+ Optional ,
10+ Sequence ,
11+ TextIO ,
12+ Tuple ,
13+ Union ,
14+ )
15+
16+ import ast
517import re
618import subprocess
719import sys
820
921
10- IGRAPH_SOURCE_FOLDER = Path .home () / "dev" / "igraph" / "igraph"
22+ IGRAPH_C_CORE_SOURCE_FOLDER = Path .home () / "dev" / "igraph" / "igraph"
23+ SOURCE_FOLDER = Path (sys .modules [__name__ ].__file__ or "" ).parent .parent .absolute ()
24+
25+
26+ def create_glob_matcher (globs : Union [str , Iterable [str ]]) -> Callable [[str ], bool ]:
27+ if isinstance (globs , str ):
28+ return create_glob_matcher ((globs ,))
29+
30+ glob_list = list (globs )
31+
32+ def result (value : str ) -> bool :
33+ return any (fnmatch (value , g ) for g in glob_list )
34+
35+ return result
1136
1237
1338def longest_common_prefix_length (items : Sequence [str ]) -> int :
39+ """Finds the length of the longest common prefix of the given list of
40+ strings.
41+ """
1442 if not items :
1543 return 0
1644
@@ -29,7 +57,54 @@ def longest_common_prefix_length(items: Sequence[str]) -> int:
2957 return best
3058
3159
32- def generate_enums (template : Path , output : Path , headers : Iterable [Path ]): # noqa: C901
60+ def reexport (
61+ input : Path ,
62+ output : Path ,
63+ module_name : str ,
64+ match : Union [str , Sequence [str ]] = "*" ,
65+ * ,
66+ template : Path = SOURCE_FOLDER / "codegen" / "reexport.py.in" ,
67+ ) -> None :
68+ """Generates a Python module that re-exports all top-level functions and
69+ classes matching the given glob or globs from another module.
70+
71+ Args:
72+ input: the module whose content is to be re-exported
73+ output: path to the source of the newly generated module
74+ match: glob or globs that the re-exported function or class names
75+ must match
76+ template: name of the template file to use for the output
77+ """
78+ with input .open () as fp :
79+ node = ast .parse (fp .read (), str (input ))
80+
81+ matcher = create_glob_matcher (g for g in match if "*" in g or "?" in g )
82+ matched_symbols = [
83+ n .name
84+ for n in node .body
85+ if isinstance (n , (ast .FunctionDef , ast .ClassDef )) and matcher (n .name )
86+ ]
87+ matched_symbols .extend (g for g in match if "*" not in g and "?" not in g )
88+ matched_symbols .sort ()
89+
90+ with output .open ("w" ) as outfp :
91+ with template .open ("r" ) as infp :
92+ outfp .write (infp .read ().format (** locals ()))
93+
94+ outfp .write (f"from { module_name } import (\n " )
95+ for symbol in matched_symbols :
96+ outfp .write (f" { symbol } ,\n " )
97+ outfp .write (")\n \n " )
98+
99+ outfp .write ("__all__ = (\n " )
100+ for symbol in matched_symbols :
101+ outfp .write (" " + repr (symbol ).replace ("'" , '"' ) + ",\n " )
102+ outfp .write (")\n " )
103+
104+
105+ def generate_enums ( # noqa: C901
106+ template : Path , output : Path , headers : Iterable [Path ]
107+ ) -> None :
33108 """Generates the contents of ``enums.py`` in the source tree by parsing
34109 the given include files from igraph's source tree.
35110
@@ -53,6 +128,7 @@ def generate_enums(template: Path, output: Path, headers: Iterable[Path]): # no
53128 "FasAlgorithm" : "FeedbackArcSetAlgorithm" ,
54129 "FileformatType" : "FileFormat" ,
55130 "LayoutDrlDefault" : "DRLLayoutPreset" ,
131+ "LazyAdlistSimplify" : "LazyAdjacencyListSimplify" ,
56132 "Loops" : None ,
57133 "Neimode" : "NeighborMode" ,
58134 "Optimal" : "Optimality" ,
@@ -193,13 +269,13 @@ def main():
193269 "-m" ,
194270 "stimulus" ,
195271 "-f" ,
196- str (IGRAPH_SOURCE_FOLDER / "interfaces" / "functions.yaml" ),
272+ str (IGRAPH_C_CORE_SOURCE_FOLDER / "interfaces" / "functions.yaml" ),
197273 "-t" ,
198- str (IGRAPH_SOURCE_FOLDER / "interfaces" / "types.yaml" ),
274+ str (IGRAPH_C_CORE_SOURCE_FOLDER / "interfaces" / "types.yaml" ),
199275 "-f" ,
200- "src/ codegen/ functions.yaml" ,
276+ str ( SOURCE_FOLDER / " codegen" / " functions.yaml") ,
201277 "-t" ,
202- "src/ codegen/ types.yaml" ,
278+ str ( SOURCE_FOLDER / " codegen" / " types.yaml") ,
203279 ]
204280
205281 args = [
@@ -209,9 +285,9 @@ def main():
209285 "-l" ,
210286 "python:ctypes" ,
211287 "-i" ,
212- "src/ codegen/ internal_lib.py.in" ,
288+ str ( SOURCE_FOLDER / " codegen" / " internal_lib.py.in") ,
213289 "-o" ,
214- "src/ igraph_ctypes/ _internal/ lib.py" ,
290+ str ( SOURCE_FOLDER / " igraph_ctypes" / " _internal" / " lib.py") ,
215291 ]
216292 ]
217293 subprocess .run (args , check = True )
@@ -223,17 +299,39 @@ def main():
223299 "-l" ,
224300 "python:ctypes-typed-wrapper" ,
225301 "-i" ,
226- "src/ codegen/ internal_functions.py.in" ,
302+ str ( SOURCE_FOLDER / " codegen" / " internal_functions.py.in") ,
227303 "-o" ,
228- "src/ igraph_ctypes/ _internal/ functions.py" ,
304+ str ( SOURCE_FOLDER / " igraph_ctypes" / " _internal" / " functions.py") ,
229305 ]
230306 ]
231307 subprocess .run (args , check = True )
232308
233309 generate_enums (
234- Path ("src/codegen/internal_enums.py.in" ),
235- Path ("src/igraph_ctypes/_internal/enums.py" ),
236- (IGRAPH_SOURCE_FOLDER / "include" ).glob ("*.h" ),
310+ SOURCE_FOLDER / "codegen" / "internal_enums.py.in" ,
311+ SOURCE_FOLDER / "igraph_ctypes" / "_internal" / "enums.py" ,
312+ (IGRAPH_C_CORE_SOURCE_FOLDER / "include" ).glob ("*.h" ),
313+ )
314+
315+ reexport (
316+ SOURCE_FOLDER / "igraph_ctypes" / "_internal" / "enums.py" ,
317+ SOURCE_FOLDER / "igraph_ctypes" / "enums.py" ,
318+ "._internal.enums" ,
319+ )
320+
321+ reexport (
322+ SOURCE_FOLDER / "igraph_ctypes" / "_internal" / "types.py" ,
323+ SOURCE_FOLDER / "igraph_ctypes" / "types.py" ,
324+ "._internal.types" ,
325+ match = (
326+ "BoolArray" ,
327+ "EdgeLike" ,
328+ "EdgeSelector" ,
329+ "IntArray" ,
330+ "RealArray" ,
331+ "VertexLike" ,
332+ "VertexPair" ,
333+ "VertexSelector" ,
334+ ),
237335 )
238336
239337
0 commit comments