init
This commit is contained in:
@@ -0,0 +1,662 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
This program will generate .pyi files for all the VTK modules
|
||||
in the "vtkmodules" package (or whichever package you specify).
|
||||
These files are used for type checking and autocompletion in
|
||||
some Python IDEs.
|
||||
|
||||
The VTK modules must be in Python's path when you run this script.
|
||||
Options are as follows:
|
||||
|
||||
-p PACKAGE The package to generate .pyi files for [vtkmodules]
|
||||
-o OUTPUT The output directory [default is the package directory]
|
||||
-e EXT The file suffix [.pyi]
|
||||
-i IMPORTER The static module importer (for static builds only)
|
||||
-h HELP
|
||||
|
||||
With no arguments, the script runs with the defaults (the .pyi files
|
||||
are put inside the existing vtkmodules package). This is equivalent
|
||||
to the following:
|
||||
|
||||
python -m vtkmodules.generate_pyi -p vtkmodules
|
||||
|
||||
To put the pyi files somewhere else, perhaps with a different suffix:
|
||||
|
||||
python -m vtkmodules.generate_pyi -o /path/to/vtkmodules -e .pyi
|
||||
|
||||
To generate pyi files for just one or two modules:
|
||||
|
||||
python -m vtkmodules.generate_pyi -p vtkmodules vtkCommonCore vtkCommonDataModel
|
||||
|
||||
To generate pyi files for your own modules in your own package:
|
||||
|
||||
python -m vtkmodules.generate_pyi -p mypackage mymodule [mymodule2 ...]
|
||||
|
||||
"""
|
||||
|
||||
from vtkmodules.vtkCommonCore import vtkObjectBase, vtkSOADataArrayTemplate
|
||||
from keyword import iskeyword
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import ast
|
||||
import argparse
|
||||
import builtins
|
||||
import inspect
|
||||
import importlib.util
|
||||
|
||||
# ==== Cancel any module overrides ====
|
||||
|
||||
import vtkmodules
|
||||
|
||||
vtkmodules.MODULE_MAPPER = {}
|
||||
|
||||
# ==== For type inspection ====
|
||||
|
||||
# list expected non-vtk type names
|
||||
types = set()
|
||||
for m,o in builtins.__dict__.items():
|
||||
if isinstance(o, type):
|
||||
types.add(m)
|
||||
for m in ['Any', 'Buffer', 'Callback', 'None', 'Pointer', 'Template', 'Union']:
|
||||
types.add(m)
|
||||
|
||||
# basic type checking methods
|
||||
ismethod = inspect.isroutine
|
||||
isclass = inspect.isclass
|
||||
|
||||
# VTK methods have a special type
|
||||
vtkmethod = type(vtkObjectBase.IsA)
|
||||
template = type(vtkSOADataArrayTemplate)
|
||||
|
||||
def isvtkmethod(m):
|
||||
"""Check for VTK's custom method descriptor"""
|
||||
return (type(m) == vtkmethod)
|
||||
|
||||
def isnamespace(m):
|
||||
"""Check for namespaces within a module"""
|
||||
# until vtkmodules.vtkCommonCore.namespace is directly accessible
|
||||
return (str(type(m)) == "<class 'vtkmodules.vtkCommonCore.namespace'>")
|
||||
|
||||
def isenum(m):
|
||||
"""Check for enums (currently derived from int)"""
|
||||
return (isclass(m) and issubclass(m, int))
|
||||
|
||||
def typename(o):
|
||||
"""Generate a typename that can be used for annotation."""
|
||||
if o is None:
|
||||
return "None"
|
||||
elif type(o) == template:
|
||||
return "Template"
|
||||
else:
|
||||
return type(o).__name__
|
||||
|
||||
def typename_forward(o):
|
||||
"""Generate a typename, or if necessary, a forward reference."""
|
||||
name = typename(o)
|
||||
if name not in types:
|
||||
# do forward reference by adding quotes
|
||||
name = '\'' + name + '\''
|
||||
return name
|
||||
|
||||
|
||||
# ==== For the topological sort ====
|
||||
|
||||
class Graph:
|
||||
"""A graph for topological sorting."""
|
||||
def __init__(self):
|
||||
self.nodes = {}
|
||||
def __getitem__(self, name):
|
||||
return self.nodes[name]
|
||||
def __setitem__(self, name, node):
|
||||
self.nodes[name] = node
|
||||
|
||||
class Node:
|
||||
"""A node for the graph."""
|
||||
def __init__(self, o, d):
|
||||
self.obj = o
|
||||
self.deps = d
|
||||
|
||||
def build_graph(d):
|
||||
"""Build a graph from a module's dictionary."""
|
||||
graph = Graph()
|
||||
items = sorted(d.items())
|
||||
for m,o in items:
|
||||
if isclass(o):
|
||||
if m == o.__name__:
|
||||
# a class definition
|
||||
bases = [b.__name__ for b in o.__bases__]
|
||||
graph[m] = Node(o, bases)
|
||||
else:
|
||||
# a class alias
|
||||
graph[m] = Node(o, [o.__name__])
|
||||
elif ismethod(o):
|
||||
graph[m] = Node(o, [])
|
||||
else:
|
||||
graph[m] = Node(o, [typename(o)])
|
||||
return graph
|
||||
|
||||
def sorted_graph_helper(graph, m, visited, items):
|
||||
"""Helper for topological sorting."""
|
||||
visited.add(m)
|
||||
try:
|
||||
node = graph[m]
|
||||
except KeyError:
|
||||
return
|
||||
for dep in node.deps:
|
||||
if dep not in visited:
|
||||
sorted_graph_helper(graph, dep, visited, items)
|
||||
items.append((m, node.obj))
|
||||
|
||||
def sorted_graph(graph):
|
||||
"""Sort a graph and return the sorted items."""
|
||||
items = []
|
||||
visited = set()
|
||||
for m in graph.nodes:
|
||||
if m not in visited:
|
||||
sorted_graph_helper(graph, m, visited, items)
|
||||
return items
|
||||
|
||||
def topologically_sorted_items(d):
|
||||
"""Return the items from a module's dictionary, topologically sorted."""
|
||||
return sorted_graph(build_graph(d))
|
||||
|
||||
|
||||
# ==== For parsing docstrings ====
|
||||
|
||||
# regular expressions for parsing
|
||||
string = re.compile(r"""("([^\\"]|\\.)*"|'([^\\']|\\.)*')""")
|
||||
identifier = re.compile(r"""([A-Za-z_]([A-Za-z0-9_]|[.][A-Za-z_])*)""")
|
||||
indent = re.compile(r"[ \t]+(?=\S)")
|
||||
has_self = re.compile(r"[(]self[,)]")
|
||||
|
||||
# important characters for rapidly parsing code
|
||||
keychar = re.compile(r"[\'\"{}\[\]()\n]")
|
||||
|
||||
def parse_error(message, text, begin, pos):
|
||||
"""Print a parse error, syntax or otherwise.
|
||||
"""
|
||||
end = text.find('\n', pos)
|
||||
if end == -1:
|
||||
end = len(text)
|
||||
sys.stderr.write("Error: " + message + ":\n")
|
||||
sys.stderr.write(text[begin:end] + "\n");
|
||||
sys.stderr.write('-' * (pos - begin) + "^\n")
|
||||
|
||||
def annotation_text(a, text, is_return):
|
||||
"""Return the new text to be used for an annotation.
|
||||
"""
|
||||
if isinstance(a, ast.Name):
|
||||
name = a.id
|
||||
if name not in types:
|
||||
# quote the type, in case it isn't yet defined
|
||||
text = '\'' + name + '\''
|
||||
elif isinstance(a, (ast.Tuple, ast.List)):
|
||||
size = len(a.elts)
|
||||
e = a.elts[0]
|
||||
offset = a.col_offset
|
||||
old_name = text[e.col_offset - offset:e.end_col_offset - offset]
|
||||
name = annotation_text(e, old_name, is_return)
|
||||
|
||||
if is_return:
|
||||
# use concrete types for return values
|
||||
if isinstance(a, ast.Tuple):
|
||||
text = 'Tuple[' + ', '.join([name]*size) + ']'
|
||||
else:
|
||||
text = 'List[' + name + ']'
|
||||
else:
|
||||
# use generic sequence types for args
|
||||
if isinstance(a, ast.Tuple):
|
||||
text = 'Sequence[' + name + ']'
|
||||
else:
|
||||
text = 'MutableSequence[' + name + ']'
|
||||
|
||||
return text
|
||||
|
||||
def fix_annotations(signature):
|
||||
"""Fix the annotations in a method definition.
|
||||
The signature must be a single-line function def, no decorators.
|
||||
"""
|
||||
# get the FunctionDef object from the parse tree
|
||||
definition = ast.parse(signature).body[0]
|
||||
annotations = [arg.annotation for arg in definition.args.args]
|
||||
return_i = len(annotations) # index of annotation for return
|
||||
annotations.append(definition.returns)
|
||||
|
||||
# create a list of changes to apply to the annotations
|
||||
changes = []
|
||||
for i,a in enumerate(annotations):
|
||||
if a is not None:
|
||||
old_text = signature[a.col_offset:a.end_col_offset]
|
||||
text = annotation_text(a, old_text, (i == return_i))
|
||||
if text != old_text:
|
||||
changes.append((a.col_offset, a.end_col_offset, text))
|
||||
|
||||
# apply changes to generate a new signature
|
||||
if changes:
|
||||
newsig = ""
|
||||
lastpos = 0
|
||||
for begin,end,text in changes:
|
||||
newsig += signature[lastpos:begin]
|
||||
newsig += text
|
||||
lastpos = end
|
||||
newsig += signature[lastpos:]
|
||||
signature = newsig
|
||||
|
||||
return signature
|
||||
|
||||
def push_signature(o, l, signature):
|
||||
"""Process a method signature and add it to the list.
|
||||
"""
|
||||
# eliminate newlines and indents
|
||||
signature = re.sub(r"\s+", " ", signature)
|
||||
# no space after opening delimiter or ':' or '='
|
||||
signature = re.sub(r"([({\[:=]) ", "\\1", signature)
|
||||
|
||||
if signature.startswith('C++:'):
|
||||
# the C++ method signatures are unused
|
||||
pass
|
||||
elif signature.startswith(o.__name__ + "("):
|
||||
# make it into a python method definition
|
||||
signature = "def " + signature + ': ...'
|
||||
if sys.hexversion >= 0x3080000:
|
||||
# XXX(Python 3.8) uses ast features from 3.8
|
||||
signature = fix_annotations(signature)
|
||||
if signature not in l:
|
||||
l.append(signature)
|
||||
|
||||
def get_signatures(o):
|
||||
"""Return a list of method signatures found in the docstring.
|
||||
"""
|
||||
doc = o.__doc__
|
||||
signatures = [] # output method signatures
|
||||
if doc is None:
|
||||
return signatures
|
||||
|
||||
# variables used for parsing the docstrings
|
||||
begin = 0 # beginning of current signature
|
||||
pos = 0 # current position in docstring
|
||||
delim_stack = [] # keep track of bracket depth
|
||||
|
||||
# loop through docstring using longest strides possible
|
||||
# (this will go line-by-line or until first ( ) { } [ ] " ')
|
||||
while pos < len(doc):
|
||||
# look for the next "character of interest" in docstring
|
||||
match = keychar.search(doc, pos)
|
||||
# did we find a match before the end of docstring?
|
||||
if match:
|
||||
# get new position
|
||||
pos,end = match.span()
|
||||
# take different action, depending on char
|
||||
c = match.group()
|
||||
if c in '\"\'':
|
||||
# skip over a string literal
|
||||
m = string.match(doc, pos)
|
||||
if m:
|
||||
pos,end = m.span()
|
||||
else:
|
||||
parse_error("Unterminated string", doc, begin, pos)
|
||||
break
|
||||
elif c in '{[(':
|
||||
# descend into a bracketed expression (push stack)
|
||||
delim_stack.append({'{':'}','[':']','(':')'}[c])
|
||||
elif c in '}])':
|
||||
# ascend out of a bracketed expression (pop stack)
|
||||
if not delim_stack or c != delim_stack.pop():
|
||||
parse_error("Unmatched bracket", doc, begin, pos)
|
||||
break
|
||||
elif c == '\n' and not (delim_stack or indent.match(doc, end)):
|
||||
# a newline not followed by an indent marks end of signature,
|
||||
# except for within brackets
|
||||
signature = doc[begin:pos].strip()
|
||||
if signature:
|
||||
push_signature(o, signatures, signature)
|
||||
begin = end
|
||||
else:
|
||||
# blank line means no more signatures in docstring
|
||||
break
|
||||
else:
|
||||
# reached the end of the docstring
|
||||
end = len(doc)
|
||||
if not delim_stack:
|
||||
signature = doc[begin:pos].strip()
|
||||
if signature:
|
||||
push_signature(o, signatures, signature)
|
||||
else:
|
||||
parse_error("Unmatched bracket", doc, begin, pos)
|
||||
break
|
||||
|
||||
# advance position within docstring and return to head of loop
|
||||
pos = end
|
||||
|
||||
return signatures
|
||||
|
||||
def get_constructors(c):
|
||||
"""Get constructors from the class documentation.
|
||||
"""
|
||||
constructors = []
|
||||
name = c.__name__
|
||||
doc = c.__doc__
|
||||
|
||||
if not doc or not doc.startswith(name + "("):
|
||||
return constructors
|
||||
signatures = get_signatures(c)
|
||||
for signature in signatures:
|
||||
if signature.startswith("def " + name + "("):
|
||||
signature = re.sub("-> \'?" + name + "\'?", "-> None", signature)
|
||||
if signature.startswith("def " + name + "()"):
|
||||
constructors.append(re.sub(name + r"\(", "__init__(self", signature, count=1))
|
||||
else:
|
||||
constructors.append(re.sub(name + r"\(", "__init__(self, ", signature, count=1))
|
||||
return constructors
|
||||
|
||||
def handle_static(o, signature):
|
||||
"""If method has no "self", add @static decorator."""
|
||||
if isvtkmethod(o) and not has_self.search(signature):
|
||||
return "@staticmethod\n" + signature
|
||||
else:
|
||||
return signature
|
||||
|
||||
def add_indent(s, indent):
|
||||
"""Add the given indent before every line in the string.
|
||||
"""
|
||||
return indent + re.sub(r"\n(?=([^\n]))", "\n" + indent, s)
|
||||
|
||||
def namespace_pyi(c, mod):
|
||||
"""Fake a namespace by creating a dummy class.
|
||||
"""
|
||||
base = "namespace"
|
||||
if mod.__name__ != 'vtkmodules.vtkCommonCore':
|
||||
base = 'vtkmodules.vtkCommonCore.' + base
|
||||
out = "class " + c.__name__ + "(" + base + "):\n"
|
||||
count = 0
|
||||
|
||||
# do all nested classes (these will be enum types)
|
||||
items = topologically_sorted_items(c.__dict__)
|
||||
others = []
|
||||
for m,o in items:
|
||||
if isenum(o) and m == o.__name__:
|
||||
out += add_indent(class_pyi(o), " ")
|
||||
count += 1
|
||||
else:
|
||||
others.append((m, o))
|
||||
|
||||
# do all constants
|
||||
items = others
|
||||
others = []
|
||||
for m,o in items:
|
||||
if not m.startswith("__") and not ismethod(o) and not isclass(o):
|
||||
out += " " + m + ":" + typename_forward(o) + "\n"
|
||||
count += 1
|
||||
else:
|
||||
others.append((m,o))
|
||||
|
||||
if count == 0:
|
||||
out = out[0:-1] + " ...\n"
|
||||
|
||||
return out
|
||||
|
||||
def class_pyi(c):
|
||||
"""Generate all the method stubs for a class.
|
||||
"""
|
||||
bases = []
|
||||
for b in c.__bases__:
|
||||
if b.__module__ in (c.__module__, 'builtins'):
|
||||
bases.append(b.__name__)
|
||||
else:
|
||||
bases.append(b.__module__ + "." + b.__name__)
|
||||
|
||||
out = "class " + c.__name__ + "(" + ", ".join(bases) + "):\n"
|
||||
count = 0
|
||||
|
||||
# do all nested classes (these are usually enum types)
|
||||
items = topologically_sorted_items(c.__dict__)
|
||||
others = []
|
||||
for m,o in items:
|
||||
if isclass(o) and m == o.__name__:
|
||||
out += add_indent(class_pyi(o), " ")
|
||||
count += 1
|
||||
else:
|
||||
others.append((m, o))
|
||||
|
||||
# do all constants
|
||||
items = others
|
||||
others = []
|
||||
for m,o in items:
|
||||
if not m.startswith("__") and not ismethod(o) and not isclass(o) and not iskeyword(m):
|
||||
out += " " + m + ":" + typename_forward(o) + "\n"
|
||||
count += 1
|
||||
else:
|
||||
others.append((m,o))
|
||||
|
||||
# do the __init__ methods
|
||||
constructors = get_constructors(c)
|
||||
if len(constructors) == 0:
|
||||
if hasattr(c, "__init__") and issubclass(c, vtkObjectBase):
|
||||
out += " def __init__(self, **properties:Any) -> None: ...\n"
|
||||
count += 1
|
||||
else:
|
||||
count += 1
|
||||
if len(constructors) == 1:
|
||||
out += add_indent(constructors[0], " ") + "\n"
|
||||
else:
|
||||
for overload in constructors:
|
||||
out += add_indent("@overload\n" + overload, " ") + "\n"
|
||||
|
||||
# do the methods
|
||||
items = others
|
||||
others = []
|
||||
for m,o in items:
|
||||
if ismethod(o):
|
||||
signatures = get_signatures(o)
|
||||
if len(signatures) == 0:
|
||||
continue
|
||||
count += 1
|
||||
if len(signatures) == 1:
|
||||
signature = handle_static(o, signatures[0])
|
||||
out += add_indent(signature, " ") + "\n"
|
||||
continue
|
||||
for overload in signatures:
|
||||
signature = handle_static(o, overload)
|
||||
out += add_indent("@overload\n" + signature, " ") + "\n"
|
||||
else:
|
||||
others.append((m, o))
|
||||
|
||||
if count == 0:
|
||||
out = out[0:-1] + " ...\n"
|
||||
|
||||
return out
|
||||
|
||||
def module_pyi(mod, output):
|
||||
"""Generate the contents of a .pyi file for a VTK module.
|
||||
"""
|
||||
# needed stuff from typing module
|
||||
output.write("from typing import overload, Any, Callable, TypeVar, Union\n")
|
||||
output.write("from typing import Tuple, List, Sequence, MutableSequence\n")
|
||||
output.write("\n")
|
||||
output.write("Callback = Union[Callable[..., None], None]\n")
|
||||
output.write("Buffer = TypeVar('Buffer')\n")
|
||||
output.write("Pointer = TypeVar('Pointer')\n")
|
||||
output.write("Template = TypeVar('Template')\n")
|
||||
output.write("\n")
|
||||
|
||||
if mod.__name__ == 'vtkmodules.vtkCommonCore':
|
||||
# dummy superclass for namespaces
|
||||
output.write("class namespace: pass\n")
|
||||
output.write("\n")
|
||||
|
||||
# all the modules this module depends on
|
||||
depends = set(['vtkmodules.vtkCommonCore'])
|
||||
for m,o in mod.__dict__.items():
|
||||
if isclass(o) and m == o.__name__:
|
||||
for base in o.__bases__:
|
||||
depends.add(base.__module__)
|
||||
depends.discard(mod.__name__)
|
||||
depends.discard("builtins")
|
||||
for depend in sorted(depends):
|
||||
output.write("import " + depend + "\n")
|
||||
if depends:
|
||||
output.write("\n")
|
||||
|
||||
# sort the dict according to dependency
|
||||
items = topologically_sorted_items(mod.__dict__)
|
||||
|
||||
# do all namespaces
|
||||
others = []
|
||||
for m,o in items:
|
||||
if isnamespace(o) and m == o.__name__:
|
||||
output.write(namespace_pyi(o, mod))
|
||||
output.write("\n")
|
||||
else:
|
||||
others.append((m, o))
|
||||
|
||||
# do all enum types
|
||||
items = others
|
||||
others = []
|
||||
for m,o in items:
|
||||
if isenum(o) and m == o.__name__:
|
||||
output.write(class_pyi(o))
|
||||
output.write("\n")
|
||||
else:
|
||||
others.append((m, o))
|
||||
|
||||
# do all enum aliases
|
||||
items = others
|
||||
others = []
|
||||
for m,o in items:
|
||||
if isenum(o) and m != o.__name__:
|
||||
output.write(m + " = " + o.__name__ + "\n")
|
||||
else:
|
||||
others.append((m, o))
|
||||
|
||||
# do all constants
|
||||
items = others
|
||||
others = []
|
||||
for m,o in items:
|
||||
if not m.startswith("__") and not ismethod(o) and not isclass(o):
|
||||
output.write(m + ":" + typename_forward(o) + "\n")
|
||||
else:
|
||||
others.append((m,o))
|
||||
if len(items) > len(others):
|
||||
output.write("\n")
|
||||
|
||||
# do all classes
|
||||
items = others
|
||||
others = []
|
||||
for m,o in items:
|
||||
if isclass(o) and m == o.__name__:
|
||||
output.write(class_pyi(o))
|
||||
output.write("\n")
|
||||
else:
|
||||
others.append((m, o))
|
||||
|
||||
# do all class aliases
|
||||
items = others
|
||||
others = []
|
||||
for m,o in items:
|
||||
if isclass(o) and m != o.__name__:
|
||||
output.write(m + " = " + o.__name__ + "\n")
|
||||
else:
|
||||
others.append((m, o))
|
||||
|
||||
def main(argv=sys.argv):
|
||||
# for error messages etcetera
|
||||
progname = os.path.basename(argv[0])
|
||||
|
||||
# parse the program arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
prog=argv[0],
|
||||
usage="python " + progname + " [-p package] [-o output_dir]",
|
||||
description="A .pyi generator for the VTK python wrappers.")
|
||||
parser.add_argument('-p', '--package', type=str, default="vtkmodules",
|
||||
help="Package name [vtkmodules].")
|
||||
parser.add_argument('-i', '--importer', type=str,
|
||||
help="Static module importer [].")
|
||||
parser.add_argument('-o', '--output', type=str,
|
||||
help="Output directory [package directory].")
|
||||
parser.add_argument('-e', '--ext', type=str, default=".pyi",
|
||||
help="Output file suffix [.pyi].")
|
||||
parser.add_argument('--test', action='count', default=0,
|
||||
help="Test .pyi files instead of creating them.")
|
||||
parser.add_argument('modules', type=str, nargs='*',
|
||||
help="Modules to process [all].")
|
||||
args = parser.parse_args(argv[1:])
|
||||
|
||||
# for convenience
|
||||
packagename = args.package
|
||||
modules = args.modules
|
||||
basedir = args.output
|
||||
ext = args.ext
|
||||
|
||||
# if static module importer is needed, it must be handled first
|
||||
if args.importer:
|
||||
if len(modules) == 0:
|
||||
sys.stderr.write(progname + ": when using '-i', all modules " +
|
||||
"in the package must be listed on the command line.\n")
|
||||
return 1
|
||||
# check that the modules aren't already present as builtins
|
||||
# (we replace '.' separators with underscores for static importers)
|
||||
module_exemplar = (packagename + '.' + modules[0]).replace('.', '_')
|
||||
if module_exemplar not in sys.builtin_module_names:
|
||||
importlib.import_module(args.importer)
|
||||
|
||||
# get information about the package
|
||||
if basedir is None or len(modules) == 0:
|
||||
mod = importlib.import_module(packagename)
|
||||
if basedir is None:
|
||||
filename = getattr(mod, '__file__', None)
|
||||
if filename is None or os.path.basename(filename) != '__init__.py':
|
||||
sys.stderr.write(progname + ": " + packagename + " has no __init__.py\n")
|
||||
return 1
|
||||
basedir = os.path.dirname(filename)
|
||||
if len(modules) == 0:
|
||||
for modname in mod.__all__:
|
||||
# only generate .pyi files for the extension modules in __all__
|
||||
try:
|
||||
spec = importlib.util.find_spec(packagename + "." + modname)
|
||||
except ValueError:
|
||||
spec = None
|
||||
if not errflag:
|
||||
errflag = True
|
||||
sys.stderr.write(progname + ": couldn't get loader for " + modname + "\n")
|
||||
if spec is None:
|
||||
continue
|
||||
if not isinstance(spec.loader, importlib.machinery.ExtensionFileLoader):
|
||||
continue
|
||||
# the module is definitely an extension module
|
||||
modules.append(modname)
|
||||
|
||||
# Give all PATH environment variable entries to add_dll_directory on Windows
|
||||
# This enable third-party libraries like OpenXR loader's DLL to be found easily.
|
||||
if os.name == "nt":
|
||||
for p in os.environ.get("PATH").split(';'):
|
||||
try:
|
||||
os.add_dll_directory(p)
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to add {p} as DLL search directory: ${e}")
|
||||
|
||||
# iterate through the modules in the package
|
||||
errflag = False
|
||||
for modname in modules:
|
||||
pyifile = os.path.join(basedir, modname + ext)
|
||||
if args.test:
|
||||
# test the syntax of the .pyi file
|
||||
flags = ast.PyCF_TYPE_COMMENTS if sys.hexversion >= 0x3080000 else 0
|
||||
with open(pyifile, 'r') as f:
|
||||
compile(f.read(), pyifile, 'exec', flags)
|
||||
else:
|
||||
# generate the .pyi file for the module
|
||||
mod = importlib.import_module(packagename + "." + modname)
|
||||
with open(pyifile, "w") as f:
|
||||
module_pyi(mod, f)
|
||||
|
||||
# add 'py.typed' to the package
|
||||
with open(os.path.join(basedir, 'py.typed'), 'w') as f:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
result = main(sys.argv)
|
||||
if result is not None:
|
||||
sys.exit(result)
|
||||
Reference in New Issue
Block a user