Skip to content

Commit 434c2a4

Browse files
committed
refactor: enums.py and types.py are now auto-generated
1 parent 3cfddab commit 434c2a4

File tree

6 files changed

+243
-29
lines changed

6 files changed

+243
-29
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ stimulus = { git = "https://github.com/igraph/stimulus.git", tag = "0.13.0" }
2121

2222
[tool.ruff]
2323
ignore = ["F405"]
24-
line-length = 80
24+
line-length = 88
2525
select = ["B", "C", "E", "F", "W"]
2626

2727
[tool.ruff.per-file-ignores]

src/codegen/reexport.py.in

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# fmt: off
2+
#
3+
# This module simply re-exports types from {module_name} that are meant to
4+
# be used by end users of the library.
5+
#
6+
# The rest of this file is generated
7+

src/codegen/run.py

Lines changed: 113 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,44 @@
1+
from fnmatch import fnmatch
12
from os.path import expanduser
23
from 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
517
import re
618
import subprocess
719
import 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

1338
def 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

src/igraph_ctypes/_internal/enums.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ class SpinglassUpdateMode(IntEnum):
296296
CONFIG = 1
297297

298298

299-
class LazyAdlistSimplify(IntEnum):
299+
class LazyAdjacencyListSimplify(IntEnum):
300300
"""Python counterpart of an ``igraph_lazy_adlist_simplify_t`` enum."""
301301

302302
DONT_SIMPLIFY = 0
@@ -548,7 +548,7 @@ class LeadingEigenvectorCommunityHistory(IntEnum):
548548
'LaplacianNormalization',
549549
'LaplacianSpectralEmbeddingType',
550550
'LayoutGrid',
551-
'LazyAdlistSimplify',
551+
'LazyAdjacencyListSimplify',
552552
'LeadingEigenvectorCommunityHistory',
553553
'Loops',
554554
'MatrixStorage',

src/igraph_ctypes/enums.py

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,120 @@
1-
"""Type specifications and aliases."""
2-
3-
# This module simply re-exports types from _internal.enums that are meant to
1+
# fmt: off
2+
#
3+
# This module simply re-exports types from ._internal.enums that are meant to
44
# be used by end users of the library.
55
#
6-
# TODO(ntamas): maybe this file should be autogenerated as well?
6+
# The rest of this file is generated
77

8-
from ._internal.enums import Connectedness
9-
from ._internal.enums import NeighborMode
8+
from ._internal.enums import (
9+
AddWeights,
10+
AdjacencyMode,
11+
AttributeCombinationType,
12+
BLISSSplittingHeuristics,
13+
BarabasiAlgorithm,
14+
ColoringGreedy,
15+
CommunityComparison,
16+
Connectedness,
17+
DRLLayoutPreset,
18+
DegreeSequenceMode,
19+
EdgeIteratorType,
20+
EdgeOrder,
21+
EdgeSequenceType,
22+
EigenAlgorithm,
23+
ErdosRenyi,
24+
ErrorType,
25+
FeedbackArcSetAlgorithm,
26+
FileFormat,
27+
FloydWarshallAlgorithm,
28+
GetAdjacency,
29+
ImitateAlgorithm,
30+
LaplacianNormalization,
31+
LaplacianSpectralEmbeddingType,
32+
LayoutGrid,
33+
LazyAdjacencyListSimplify,
34+
LeadingEigenvectorCommunityHistory,
35+
Loops,
36+
MatrixStorage,
37+
Multiple,
38+
NeighborMode,
39+
Optimality,
40+
Order,
41+
PagerankAlgorithm,
42+
RandomTreeMethod,
43+
RandomWalkStuck,
44+
RealizeDegseq,
45+
Reciprocity,
46+
Rewiring,
47+
RootChoice,
48+
SparseMatrixSolver,
49+
SparseMatrixType,
50+
SpinglassImplementation,
51+
SpinglassUpdateMode,
52+
StarMode,
53+
SubgraphImplementation,
54+
ToDirected,
55+
ToUndirected,
56+
TransitivityMode,
57+
TreeMode,
58+
VconnNei,
59+
VertexIteratorType,
60+
VertexSequenceType,
61+
VoronoiTiebreaker,
62+
WheelMode,
63+
)
1064

11-
__all__ = ("Connectedness", "NeighborMode")
65+
__all__ = (
66+
"AddWeights",
67+
"AdjacencyMode",
68+
"AttributeCombinationType",
69+
"BLISSSplittingHeuristics",
70+
"BarabasiAlgorithm",
71+
"ColoringGreedy",
72+
"CommunityComparison",
73+
"Connectedness",
74+
"DRLLayoutPreset",
75+
"DegreeSequenceMode",
76+
"EdgeIteratorType",
77+
"EdgeOrder",
78+
"EdgeSequenceType",
79+
"EigenAlgorithm",
80+
"ErdosRenyi",
81+
"ErrorType",
82+
"FeedbackArcSetAlgorithm",
83+
"FileFormat",
84+
"FloydWarshallAlgorithm",
85+
"GetAdjacency",
86+
"ImitateAlgorithm",
87+
"LaplacianNormalization",
88+
"LaplacianSpectralEmbeddingType",
89+
"LayoutGrid",
90+
"LazyAdjacencyListSimplify",
91+
"LeadingEigenvectorCommunityHistory",
92+
"Loops",
93+
"MatrixStorage",
94+
"Multiple",
95+
"NeighborMode",
96+
"Optimality",
97+
"Order",
98+
"PagerankAlgorithm",
99+
"RandomTreeMethod",
100+
"RandomWalkStuck",
101+
"RealizeDegseq",
102+
"Reciprocity",
103+
"Rewiring",
104+
"RootChoice",
105+
"SparseMatrixSolver",
106+
"SparseMatrixType",
107+
"SpinglassImplementation",
108+
"SpinglassUpdateMode",
109+
"StarMode",
110+
"SubgraphImplementation",
111+
"ToDirected",
112+
"ToUndirected",
113+
"TransitivityMode",
114+
"TreeMode",
115+
"VconnNei",
116+
"VertexIteratorType",
117+
"VertexSequenceType",
118+
"VoronoiTiebreaker",
119+
"WheelMode",
120+
)

src/igraph_ctypes/types.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
"""Type specifications and aliases."""
2-
3-
# This module simply re-exports types from _internal.types that are meant to
1+
# fmt: off
2+
#
3+
# This module simply re-exports types from ._internal.types that are meant to
44
# be used by end users of the library.
55
#
6-
# TODO(ntamas): maybe this file should be autogenerated as well?
6+
# The rest of this file is generated
77

88
from ._internal.types import (
99
BoolArray,

0 commit comments

Comments
 (0)