Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e46c960

Browse files
committed
Improve geninterop script to handle new case in 3.11
1 parent 9412af9 commit e46c960

File tree

1 file changed

+102
-71
lines changed

1 file changed

+102
-71
lines changed

tools/geninterop/geninterop.py

100644100755
+102-71
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,26 @@
1313
- clang
1414
"""
1515

16-
from __future__ import print_function
17-
18-
import logging
1916
import os
17+
import shutil
2018
import sys
2119
import sysconfig
2220
import subprocess
2321

24-
if sys.version_info.major > 2:
25-
from io import StringIO
26-
else:
27-
from StringIO import StringIO
28-
22+
from io import StringIO
23+
from pathlib import Path
2924
from pycparser import c_ast, c_parser
3025

31-
_log = logging.getLogger()
32-
logging.basicConfig(level=logging.DEBUG)
33-
34-
PY_MAJOR = sys.version_info[0]
35-
PY_MINOR = sys.version_info[1]
36-
3726
# rename some members from their C name when generating the C#
3827
_typeoffset_member_renames = {
3928
"ht_name": "name",
40-
"ht_qualname": "qualname"
29+
"ht_qualname": "qualname",
30+
"getitem": "spec_cache_getitem",
4131
}
4232

4333

4434
def _check_output(*args, **kwargs):
45-
"""Check output wrapper for py2/py3 compatibility"""
46-
output = subprocess.check_output(*args, **kwargs)
47-
if PY_MAJOR == 2:
48-
return output
49-
return output.decode("ascii")
35+
return subprocess.check_output(*args, **kwargs, encoding="utf8")
5036

5137

5238
class AstParser(object):
@@ -92,7 +78,7 @@ def visit(self, node):
9278
self.visit_identifier(node)
9379

9480
def visit_ast(self, ast):
95-
for name, node in ast.children():
81+
for _name, node in ast.children():
9682
self.visit(node)
9783

9884
def visit_typedef(self, typedef):
@@ -113,7 +99,7 @@ def visit_struct(self, struct):
11399
self.visit(decl)
114100
self._struct_members_stack.pop(0)
115101
self._struct_stack.pop(0)
116-
elif self._ptr_decl_depth:
102+
elif self._ptr_decl_depth or self._struct_members_stack:
117103
# the struct is empty, but add it as a member to the current
118104
# struct as the current member maybe a pointer to it.
119105
self._add_struct_member(struct.name)
@@ -141,7 +127,8 @@ def _add_struct_member(self, type_name):
141127
current_struct = self._struct_stack[0]
142128
member_name = self._struct_members_stack[0]
143129
struct_members = self._struct_members.setdefault(
144-
self._get_struct_name(current_struct), [])
130+
self._get_struct_name(current_struct), []
131+
)
145132

146133
# get the node associated with this type
147134
node = None
@@ -179,7 +166,6 @@ def _get_struct_name(self, node):
179166

180167

181168
class Writer(object):
182-
183169
def __init__(self):
184170
self._stream = StringIO()
185171

@@ -193,43 +179,65 @@ def to_string(self):
193179
return self._stream.getvalue()
194180

195181

196-
def preprocess_python_headers():
182+
def preprocess_python_headers(*, cc=None, include_py=None):
197183
"""Return Python.h pre-processed, ready for parsing.
198184
Requires clang.
199185
"""
200-
fake_libc_include = os.path.join(os.path.dirname(__file__),
201-
"fake_libc_include")
186+
this_path = Path(__file__).parent
187+
188+
fake_libc_include = this_path / "fake_libc_include"
202189
include_dirs = [fake_libc_include]
203190

204-
include_py = sysconfig.get_config_var("INCLUDEPY")
191+
if cc is None:
192+
cc = shutil.which("clang")
193+
if cc is None:
194+
cc = shutil.which("gcc")
195+
if cc is None:
196+
raise RuntimeError("No suitable C compiler found, need clang or gcc")
197+
198+
if include_py is None:
199+
include_py = sysconfig.get_config_var("INCLUDEPY")
200+
include_py = Path(include_py)
201+
205202
include_dirs.append(include_py)
206203

207-
include_args = [c for p in include_dirs for c in ["-I", p]]
204+
include_args = [c for p in include_dirs for c in ["-I", str(p)]]
208205

209206
defines = [
210-
"-D", "__attribute__(x)=",
211-
"-D", "__inline__=inline",
212-
"-D", "__asm__=;#pragma asm",
213-
"-D", "__int64=long long",
214-
"-D", "_POSIX_THREADS"
207+
"-D",
208+
"__attribute__(x)=",
209+
"-D",
210+
"__inline__=inline",
211+
"-D",
212+
"__asm__=;#pragma asm",
213+
"-D",
214+
"__int64=long long",
215+
"-D",
216+
"_POSIX_THREADS",
215217
]
216218

217-
if os.name == 'nt':
218-
defines.extend([
219-
"-D", "__inline=inline",
220-
"-D", "__ptr32=",
221-
"-D", "__ptr64=",
222-
"-D", "__declspec(x)=",
223-
])
219+
if os.name == "nt":
220+
defines.extend(
221+
[
222+
"-D",
223+
"__inline=inline",
224+
"-D",
225+
"__ptr32=",
226+
"-D",
227+
"__ptr64=",
228+
"-D",
229+
"__declspec(x)=",
230+
]
231+
)
224232

225233
if hasattr(sys, "abiflags"):
226234
if "d" in sys.abiflags:
227235
defines.extend(("-D", "PYTHON_WITH_PYDEBUG"))
228236
if "u" in sys.abiflags:
229237
defines.extend(("-D", "PYTHON_WITH_WIDE_UNICODE"))
230238

231-
python_h = os.path.join(include_py, "Python.h")
232-
cmd = ["clang", "-pthread"] + include_args + defines + ["-E", python_h]
239+
python_h = include_py / "Python.h"
240+
cmd = [cc, "-pthread"] + include_args + defines + ["-E", str(python_h)]
233241

234242
# normalize as the parser doesn't like windows line endings.
235243
lines = []
@@ -240,16 +248,13 @@ def preprocess_python_headers():
240248
return "\n".join(lines)
241249

242250

243-
244-
def gen_interop_head(writer):
251+
def gen_interop_head(writer, version, abi_flags):
245252
filename = os.path.basename(__file__)
246-
abi_flags = getattr(sys, "abiflags", "").replace("m", "")
247-
py_ver = "{0}.{1}".format(PY_MAJOR, PY_MINOR)
248-
class_definition = """
249-
// Auto-generated by %s.
253+
class_definition = f"""
254+
// Auto-generated by {filename}.
250255
// DO NOT MODIFY BY HAND.
251256
252-
// Python %s: ABI flags: '%s'
257+
// Python {".".join(version[:2])}: ABI flags: '{abi_flags}'
253258
254259
// ReSharper disable InconsistentNaming
255260
// ReSharper disable IdentifierTypo
@@ -261,7 +266,7 @@ def gen_interop_head(writer):
261266
using Python.Runtime.Native;
262267
263268
namespace Python.Runtime
264-
{""" % (filename, py_ver, abi_flags)
269+
{{"""
265270
writer.extend(class_definition)
266271

267272

@@ -271,25 +276,24 @@ def gen_interop_tail(writer):
271276
writer.extend(tail)
272277

273278

274-
def gen_heap_type_members(parser, writer, type_name = None):
279+
def gen_heap_type_members(parser, writer, type_name):
275280
"""Generate the TypeOffset C# class"""
276281
members = parser.get_struct_members("PyHeapTypeObject")
277-
type_name = type_name or "TypeOffset{0}{1}".format(PY_MAJOR, PY_MINOR)
278-
class_definition = """
282+
class_definition = f"""
279283
[SuppressMessage("Style", "IDE1006:Naming Styles",
280284
Justification = "Following CPython",
281285
Scope = "type")]
282286
283287
[StructLayout(LayoutKind.Sequential)]
284-
internal class {0} : GeneratedTypeOffsets, ITypeOffsets
288+
internal class {type_name} : GeneratedTypeOffsets, ITypeOffsets
285289
{{
286-
public {0}() {{ }}
290+
public {type_name}() {{ }}
287291
// Auto-generated from PyHeapTypeObject in Python.h
288-
""".format(type_name)
292+
"""
289293

290294
# All the members are sizeof(void*) so we don't need to do any
291295
# extra work to determine the size based on the type.
292-
for name, tpy in members:
296+
for name, _type in members:
293297
name = _typeoffset_member_renames.get(name, name)
294298
class_definition += " public int %s { get; private set; }\n" % name
295299

@@ -304,17 +308,18 @@ def gen_structure_code(parser, writer, type_name, indent):
304308
return False
305309
out = writer.append
306310
out(indent, "[StructLayout(LayoutKind.Sequential)]")
307-
out(indent, "internal struct %s" % type_name)
311+
out(indent, f"internal struct {type_name}")
308312
out(indent, "{")
309-
for name, tpy in members:
310-
out(indent + 1, "public IntPtr %s;" % name)
313+
for name, _type in members:
314+
out(indent + 1, f"public IntPtr {name};")
311315
out(indent, "}")
312316
out()
313317
return True
314318

315-
def main():
319+
320+
def main(*, cc=None, include_py=None, version=None, out=None):
316321
# preprocess Python.h and build the AST
317-
python_h = preprocess_python_headers()
322+
python_h = preprocess_python_headers(cc=cc, include_py=include_py)
318323
parser = c_parser.CParser()
319324
ast = parser.parse(python_h)
320325

@@ -323,21 +328,47 @@ def main():
323328
ast_parser.visit(ast)
324329

325330
writer = Writer()
331+
332+
if include_py and not version:
333+
raise RuntimeError("If the include path is overridden, version must be "
334+
"defined"
335+
)
336+
337+
if version:
338+
version = version.split('.')
339+
else:
340+
version = sys.version_info
341+
326342
# generate the C# code
327-
offsets_type_name = "NativeTypeOffset" if len(sys.argv) > 1 else None
328-
gen_interop_head(writer)
343+
abi_flags = getattr(sys, "abiflags", "").replace("m", "")
344+
gen_interop_head(writer, version, abi_flags)
329345

330-
gen_heap_type_members(ast_parser, writer, type_name = offsets_type_name)
346+
type_name = f"TypeOffset{version[0]}{version[1]}{abi_flags}"
347+
gen_heap_type_members(ast_parser, writer, type_name)
331348

332349
gen_interop_tail(writer)
333350

334351
interop_cs = writer.to_string()
335-
if len(sys.argv) > 1:
336-
with open(sys.argv[1], "w") as fh:
337-
fh.write(interop_cs)
338-
else:
352+
if not out or out == "-":
339353
print(interop_cs)
354+
else:
355+
with open(out, "w") as fh:
356+
fh.write(interop_cs)
340357

341358

342359
if __name__ == "__main__":
343-
sys.exit(main())
360+
import argparse
361+
362+
a = argparse.ArgumentParser("Interop file generator for Python.NET")
363+
a.add_argument("--cc", help="C compiler to use, either clang or gcc")
364+
a.add_argument("--include-py", help="Include path of Python")
365+
a.add_argument("--version", help="Python version")
366+
a.add_argument("--out", help="Output path", default="-")
367+
args = a.parse_args()
368+
369+
sys.exit(main(
370+
cc=args.cc,
371+
include_py=args.include_py,
372+
out=args.out,
373+
version=args.version
374+
))

0 commit comments

Comments
 (0)