tinycwrap 0.0.1__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/PKG-INFO +1 -1
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/pyproject.toml +1 -1
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tests/test_geom.py +21 -1
- tinycwrap-0.0.3/tests/test_path.py +41 -0
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tinycwrap/__init__.py +1 -1
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tinycwrap/cmodule.py +135 -22
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tinycwrap/parsing.py +1 -9
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tinycwrap.egg-info/PKG-INFO +1 -1
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tinycwrap.egg-info/SOURCES.txt +1 -0
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/README.md +0 -0
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/setup.cfg +0 -0
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tests/test_examples.py +0 -0
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tinycwrap.egg-info/dependency_links.txt +0 -0
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tinycwrap.egg-info/requires.txt +0 -0
- {tinycwrap-0.0.1 → tinycwrap-0.0.3}/tinycwrap.egg-info/top_level.txt +0 -0
|
@@ -40,4 +40,24 @@ def test_geom2d_return_strct(cg):
|
|
|
40
40
|
assert seg.data[0] == 0.1
|
|
41
41
|
assert seg.data[1] == 0.2
|
|
42
42
|
assert np.isclose(seg.data[2], 0.1+0.3*0.5)
|
|
43
|
-
assert np.isclose(seg.data[3], 0.2+0.4*0.3)
|
|
43
|
+
assert np.isclose(seg.data[3], 0.2+0.4*0.3)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_geom2d_return_strct_array_member(cg):
|
|
47
|
+
seg = cg.geom2d_rectangle_to_path(1,2)
|
|
48
|
+
assert len(seg) == 4
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_docstring_contains_contract(cg):
|
|
52
|
+
doc = cg.geom2d_rectangle_to_path.__doc__
|
|
53
|
+
assert "Contract: len(out_segments)=4" in doc
|
|
54
|
+
assert "Auto-wrapped C function `geom2d_rectangle_to_path`." in doc
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_polygon_length(cg):
|
|
58
|
+
pts = cg.G2DPoints(len_points=3)
|
|
59
|
+
pts.points[0]["x"] = 0.0; pts.points[0]["y"] = 0.0
|
|
60
|
+
pts.points[1]["x"] = 1.0; pts.points[1]["y"] = 0.0
|
|
61
|
+
pts.points[2]["x"] = 1.0; pts.points[2]["y"] = 1.0
|
|
62
|
+
length = cg.geom2d_polygon_length(pts.points)
|
|
63
|
+
assert length == 2.0
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from tinycwrap import CModule
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture(scope="module")
|
|
10
|
+
def cp():
|
|
11
|
+
return CModule(Path("tests/t1/base.c"), Path("tests/t1/path.c"))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_path_get_steps_contract(cp):
|
|
15
|
+
seg = cp.G2DSegment()
|
|
16
|
+
# line from (0,0) to (1,0)
|
|
17
|
+
seg.type = 0
|
|
18
|
+
seg.data = [0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
|
19
|
+
segments = np.array([seg._data], dtype=cp.G2DSegment.dtype)
|
|
20
|
+
steps = cp.geom2d_path_get_steps(segments, ds_min=0.25)
|
|
21
|
+
expected_len = cp.geom2d_path_get_len_steps(segments, len_segments=len(segments), ds_min=0.25)
|
|
22
|
+
assert len(steps) == expected_len
|
|
23
|
+
assert np.isclose(steps[-1], cp.geom2d_path_get_length(segments, len_segments=1))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_path_struct_pointer_array(cp):
|
|
27
|
+
path = cp.G2DPath(len_segments=3)
|
|
28
|
+
assert path.len_segments == 3
|
|
29
|
+
assert isinstance(path.segments, np.ndarray)
|
|
30
|
+
assert path.segments.shape == (3,)
|
|
31
|
+
assert path.segments.dtype == cp.G2DSegment.dtype
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_return_class_path(cp):
|
|
35
|
+
path=cp.G2DPath(len_segments=1)
|
|
36
|
+
cp.geom2d_path_from_circle(1.0, path)
|
|
37
|
+
assert path.len_segments == 1
|
|
38
|
+
assert path.segments.shape == (1,)
|
|
39
|
+
seg = path.segments[0]
|
|
40
|
+
assert seg['type'] == 1 # circle segment
|
|
41
|
+
np.testing.assert_allclose(seg['data'][:3], [0.0, 0.0, 1.0]) # cx,cy,radius
|
|
@@ -53,6 +53,7 @@ class CModule:
|
|
|
53
53
|
compiler="gcc",
|
|
54
54
|
compile_args=None,
|
|
55
55
|
reload=True,
|
|
56
|
+
verbose=False,
|
|
56
57
|
):
|
|
57
58
|
"""
|
|
58
59
|
Parameters
|
|
@@ -69,6 +70,8 @@ class CModule:
|
|
|
69
70
|
reload : bool, default True
|
|
70
71
|
If True and running inside IPython, register a pre-run-cell hook to auto-recompile
|
|
71
72
|
when the C source changes.
|
|
73
|
+
verbose : bool, default False
|
|
74
|
+
If True, print compilation and parsing details.
|
|
72
75
|
"""
|
|
73
76
|
if not c_sources:
|
|
74
77
|
raise ValueError("At least one C source path is required")
|
|
@@ -80,6 +83,7 @@ class CModule:
|
|
|
80
83
|
"compile_args": compile_args
|
|
81
84
|
or ["-O3", "-shared", "-fPIC", "-march=native", "-mtune=native"],
|
|
82
85
|
}
|
|
86
|
+
self._verbose = verbose
|
|
83
87
|
|
|
84
88
|
self._ffi = None
|
|
85
89
|
self._lib = None
|
|
@@ -136,12 +140,13 @@ class CModule:
|
|
|
136
140
|
cmd.extend(["-I", str(inc)])
|
|
137
141
|
|
|
138
142
|
# regenerate cdef from source
|
|
139
|
-
self._cdef = self._generate_cdef_from_source(verbose=self.
|
|
143
|
+
self._cdef = self._generate_cdef_from_source(verbose=self._verbose)
|
|
140
144
|
|
|
141
145
|
sources = [str(self._c_path), *(str(p) for p in self._extra_sources)]
|
|
142
146
|
cmd.extend(["-o", str(so_path), *sources])
|
|
143
147
|
|
|
144
|
-
|
|
148
|
+
if self._verbose:
|
|
149
|
+
print(f"[CModule] Compiling: {' '.join(cmd)}")
|
|
145
150
|
subprocess.run(cmd, check=True)
|
|
146
151
|
|
|
147
152
|
ffi = FFI()
|
|
@@ -153,7 +158,8 @@ class CModule:
|
|
|
153
158
|
self._sig = sig
|
|
154
159
|
self._so_path = so_path
|
|
155
160
|
|
|
156
|
-
|
|
161
|
+
if self._verbose:
|
|
162
|
+
print(f"[CModule] Loaded {so_path}")
|
|
157
163
|
|
|
158
164
|
# Re-parse cdef and attach docs from C source, then create wrappers
|
|
159
165
|
self._func_specs = parse_functions_from_cdef(self._cdef)
|
|
@@ -187,6 +193,23 @@ class CModule:
|
|
|
187
193
|
self._so_path = None
|
|
188
194
|
self._compile_and_load()
|
|
189
195
|
|
|
196
|
+
def _debug_specs(self):
|
|
197
|
+
"""Return a pretty string of parsed function specs and contracts (for debugging)."""
|
|
198
|
+
lines: list[str] = []
|
|
199
|
+
for name, spec in self._func_specs.items():
|
|
200
|
+
arg_lines = [f" {a.raw_ctype} {a.name}" for a in spec.args]
|
|
201
|
+
if arg_lines:
|
|
202
|
+
lines.append(f"{name}")
|
|
203
|
+
lines.extend(arg_lines)
|
|
204
|
+
lines.append(f" -> {spec.return_ctype}")
|
|
205
|
+
else:
|
|
206
|
+
lines.append(f"{name}() -> {spec.return_ctype}")
|
|
207
|
+
if spec.contracts:
|
|
208
|
+
for target, expr, is_post in spec.contracts:
|
|
209
|
+
prefix = "Post-Contract" if is_post else "Contract"
|
|
210
|
+
lines.append(f" {prefix}: {target} = {expr}")
|
|
211
|
+
print("\n".join(lines))
|
|
212
|
+
|
|
190
213
|
# ---------- IPython auto-reload hook ------------------------------------
|
|
191
214
|
|
|
192
215
|
def _register_ipython_autoreload(self):
|
|
@@ -230,7 +253,7 @@ class CModule:
|
|
|
230
253
|
|
|
231
254
|
# ---------- 1) auto-generate cdef from C source -------------------------
|
|
232
255
|
|
|
233
|
-
def _generate_cdef_from_source(self, verbose: bool =
|
|
256
|
+
def _generate_cdef_from_source(self, verbose: bool | None = None) -> str:
|
|
234
257
|
"""
|
|
235
258
|
Parse the C file and auto-generate a minimal cdef string.
|
|
236
259
|
|
|
@@ -334,21 +357,21 @@ class CModule:
|
|
|
334
357
|
continue
|
|
335
358
|
cdef_lines.append(line)
|
|
336
359
|
cdef = "\n".join(cdef_lines)
|
|
337
|
-
if verbose
|
|
360
|
+
show = self._verbose if verbose is None else verbose
|
|
361
|
+
if show:
|
|
338
362
|
print("[CModule] Auto-generated cdef:\n" + cdef)
|
|
339
363
|
return cdef
|
|
340
364
|
|
|
341
365
|
# ---------- 2) parse cdef -> FuncSpec/ArgSpec ---------------------------
|
|
342
366
|
|
|
343
367
|
def _attach_docs_from_source(self):
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
except OSError:
|
|
368
|
+
src = self._combined_source()
|
|
369
|
+
if not src:
|
|
347
370
|
return
|
|
348
371
|
|
|
349
372
|
for fname, fspec in self._func_specs.items():
|
|
350
|
-
# we look for: name ( ... ) /* ... */
|
|
351
|
-
pattern = rf"{re.escape(fname)}\s*\([^{{;]*\)\s*/\*(.*?)\*/"
|
|
373
|
+
# we look for: name ( ... ) [optional brace] /* ... */
|
|
374
|
+
pattern = rf"{re.escape(fname)}\s*\([^{{;]*\)\s*\{{?\s*/\*(.*?)\*/"
|
|
352
375
|
m = re.search(pattern, src, flags=re.DOTALL)
|
|
353
376
|
if not m:
|
|
354
377
|
continue
|
|
@@ -439,8 +462,19 @@ class CModule:
|
|
|
439
462
|
for sname, sspec in self._struct_specs.items():
|
|
440
463
|
try:
|
|
441
464
|
dtype_fields = []
|
|
465
|
+
pointer_meta = []
|
|
442
466
|
for f in sspec.fields:
|
|
443
|
-
|
|
467
|
+
try:
|
|
468
|
+
base_dtype = numpy_dtype_for_base_type(f.base_type)
|
|
469
|
+
except TypeError:
|
|
470
|
+
base_dtype = None
|
|
471
|
+
if f.is_pointer:
|
|
472
|
+
dtype_fields.append((f.name, np.uintp))
|
|
473
|
+
pointer_meta.append(f)
|
|
474
|
+
if base_dtype is None:
|
|
475
|
+
continue
|
|
476
|
+
if base_dtype is None:
|
|
477
|
+
continue
|
|
444
478
|
if f.array_len:
|
|
445
479
|
dtype_fields.append((f.name, (base_dtype, (f.array_len,))))
|
|
446
480
|
else:
|
|
@@ -449,15 +483,35 @@ class CModule:
|
|
|
449
483
|
except TypeError:
|
|
450
484
|
continue
|
|
451
485
|
|
|
452
|
-
def make_struct_class(spec, dtype):
|
|
453
|
-
slots = ("_data",)
|
|
486
|
+
def make_struct_class(spec, dtype, _struct_dtypes=self._struct_dtypes):
|
|
487
|
+
slots = ("_data", "_children")
|
|
454
488
|
|
|
455
489
|
def __init__(self, **kwargs):
|
|
456
490
|
data = np.zeros((), dtype=dtype)
|
|
491
|
+
children = {}
|
|
492
|
+
# handle pointer+len convention
|
|
493
|
+
for f in spec.fields:
|
|
494
|
+
if f.is_pointer and f.name in dtype.names:
|
|
495
|
+
len_field = f"len_{f.name}"
|
|
496
|
+
target_dtype = _struct_dtypes.get(f.base_type)
|
|
497
|
+
provided = kwargs.get(f.name)
|
|
498
|
+
if provided is not None and target_dtype is not None:
|
|
499
|
+
arr = np.ascontiguousarray(provided, dtype=target_dtype)
|
|
500
|
+
children[f.name] = arr
|
|
501
|
+
data[f.name] = arr.ctypes.data
|
|
502
|
+
if len_field in dtype.names and len_field not in kwargs:
|
|
503
|
+
data[len_field] = len(arr)
|
|
504
|
+
elif len_field in kwargs and target_dtype is not None:
|
|
505
|
+
llen = int(kwargs[len_field])
|
|
506
|
+
arr = np.zeros(llen, dtype=target_dtype)
|
|
507
|
+
children[f.name] = arr
|
|
508
|
+
data[f.name] = arr.ctypes.data
|
|
509
|
+
data[len_field] = llen
|
|
457
510
|
for k in dtype.names:
|
|
458
511
|
if k in kwargs:
|
|
459
512
|
data[k] = kwargs[k]
|
|
460
513
|
object.__setattr__(self, "_data", data)
|
|
514
|
+
object.__setattr__(self, "_children", children)
|
|
461
515
|
|
|
462
516
|
def __repr__(self):
|
|
463
517
|
parts_list = []
|
|
@@ -485,7 +539,9 @@ class CModule:
|
|
|
485
539
|
field_info = dtype.fields[fname][0]
|
|
486
540
|
is_scalar = field_info.shape == ()
|
|
487
541
|
|
|
488
|
-
def getter(self, fname=fname, is_scalar=is_scalar):
|
|
542
|
+
def getter(self, fname=fname, is_scalar=is_scalar, field_info=field_info):
|
|
543
|
+
if fname in getattr(self, "_children", {}):
|
|
544
|
+
return self._children[fname]
|
|
489
545
|
val = self._data[fname]
|
|
490
546
|
return val.item() if is_scalar else val
|
|
491
547
|
|
|
@@ -496,6 +552,11 @@ class CModule:
|
|
|
496
552
|
field_info=field_info,
|
|
497
553
|
is_scalar=is_scalar,
|
|
498
554
|
):
|
|
555
|
+
if fname in getattr(self, "_children", {}):
|
|
556
|
+
arr = np.ascontiguousarray(value, dtype=self._children[fname].dtype)
|
|
557
|
+
self._children[fname] = arr
|
|
558
|
+
self._data[fname] = arr.ctypes.data
|
|
559
|
+
return
|
|
499
560
|
if is_scalar:
|
|
500
561
|
self._data[fname] = value
|
|
501
562
|
else:
|
|
@@ -550,7 +611,7 @@ class CModule:
|
|
|
550
611
|
wrapper = namespace[fspec.name]
|
|
551
612
|
wrapper.__source__ = src
|
|
552
613
|
try:
|
|
553
|
-
wrapper.__c_source__ = self.
|
|
614
|
+
wrapper.__c_source__ = self._function_source(fspec.name) or self._combined_source()
|
|
554
615
|
except OSError:
|
|
555
616
|
wrapper.__c_source__ = None
|
|
556
617
|
return wrapper
|
|
@@ -560,10 +621,13 @@ class CModule:
|
|
|
560
621
|
Build the Python source string for a wrapper with an explicit signature.
|
|
561
622
|
Keeping this separate allows inspection/debugging of the generated code.
|
|
562
623
|
"""
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
c_source_text =
|
|
624
|
+
src_parts: list[str] = []
|
|
625
|
+
func_text = self._function_source(fspec.name)
|
|
626
|
+
if func_text:
|
|
627
|
+
c_source_text = func_text
|
|
628
|
+
else:
|
|
629
|
+
combined = self._combined_source()
|
|
630
|
+
c_source_text = combined if combined else None
|
|
567
631
|
|
|
568
632
|
struct_names = set(self._struct_specs.keys())
|
|
569
633
|
params: list[str] = []
|
|
@@ -662,11 +726,27 @@ class CModule:
|
|
|
662
726
|
)
|
|
663
727
|
}
|
|
664
728
|
|
|
729
|
+
func_names = set(self._func_specs.keys())
|
|
730
|
+
|
|
665
731
|
def _expr_py(expr: str) -> str:
|
|
666
732
|
expr = expr.strip()
|
|
667
733
|
expr = re.sub(r"len\((\w+)\)", r"len(arr_\1)", expr)
|
|
668
734
|
for name in pointer_scalar_names:
|
|
669
735
|
expr = re.sub(rf"\b{name}\b", f"int(arr_{name}.ravel()[0])", expr)
|
|
736
|
+
if func_names:
|
|
737
|
+
pattern = r"\b(" + "|".join(re.escape(n) for n in func_names) + r")\s*\(([^()]*)\)"
|
|
738
|
+
|
|
739
|
+
def _repl(m):
|
|
740
|
+
fname = m.group(1)
|
|
741
|
+
spec = self._func_specs.get(fname)
|
|
742
|
+
if not spec:
|
|
743
|
+
return m.group(0)
|
|
744
|
+
call_parts = []
|
|
745
|
+
for arg in spec.args:
|
|
746
|
+
call_parts.append(f"{arg.name}={arg.name}")
|
|
747
|
+
return f"_self.{fname}(" + ", ".join(call_parts) + ")"
|
|
748
|
+
|
|
749
|
+
expr = re.sub(pattern, _repl, expr)
|
|
670
750
|
return expr
|
|
671
751
|
|
|
672
752
|
call_args: list[str] = []
|
|
@@ -746,12 +826,12 @@ class CModule:
|
|
|
746
826
|
]
|
|
747
827
|
if a.array_len is not None:
|
|
748
828
|
out_lines += [
|
|
749
|
-
f" arr_{a.name} = np.
|
|
829
|
+
f" arr_{a.name} = np.zeros({int(a.array_len)}, dtype=base_dtype)"
|
|
750
830
|
]
|
|
751
831
|
elif a.name in contract_map:
|
|
752
832
|
expr_py = _expr_py(contract_map[a.name])
|
|
753
833
|
out_lines += [
|
|
754
|
-
f" arr_{a.name} = np.
|
|
834
|
+
f" arr_{a.name} = np.zeros(int({expr_py}), dtype=base_dtype)"
|
|
755
835
|
]
|
|
756
836
|
elif a.array_len is None and a.name not in contract_map:
|
|
757
837
|
out_lines += [
|
|
@@ -761,7 +841,7 @@ class CModule:
|
|
|
761
841
|
out_lines += [
|
|
762
842
|
f" ref_arr = locals().get('arr_{ref_name}', None)",
|
|
763
843
|
" if ref_arr is not None:",
|
|
764
|
-
f" arr_{a.name} = np.
|
|
844
|
+
f" arr_{a.name} = np.zeros_like(ref_arr, dtype=base_dtype)",
|
|
765
845
|
" else:",
|
|
766
846
|
f" raise ValueError('{fspec.name}: provide {a.name} or a Contract for its length')",
|
|
767
847
|
]
|
|
@@ -912,3 +992,36 @@ class CModule:
|
|
|
912
992
|
lines.append(f" return {ret_expr}")
|
|
913
993
|
|
|
914
994
|
return "\n".join(lines)
|
|
995
|
+
|
|
996
|
+
def _combined_source(self) -> str:
|
|
997
|
+
texts: list[str] = []
|
|
998
|
+
for path in [self._c_path, *self._extra_sources]:
|
|
999
|
+
try:
|
|
1000
|
+
texts.append(path.read_text(encoding="utf8"))
|
|
1001
|
+
except OSError:
|
|
1002
|
+
continue
|
|
1003
|
+
return "\n".join(texts)
|
|
1004
|
+
|
|
1005
|
+
def _function_source(self, fname: str) -> str | None:
|
|
1006
|
+
"""Return the source (including body) of the given C function if found."""
|
|
1007
|
+
src = self._combined_source()
|
|
1008
|
+
if not src:
|
|
1009
|
+
return None
|
|
1010
|
+
pattern = re.compile(
|
|
1011
|
+
rf"([\w\s\*\r\n]+{re.escape(fname)}\s*\([^{{;]*\))\s*(?:/\*.*?\*/\s*)?\{{",
|
|
1012
|
+
re.DOTALL,
|
|
1013
|
+
)
|
|
1014
|
+
m = pattern.search(src)
|
|
1015
|
+
if not m:
|
|
1016
|
+
return None
|
|
1017
|
+
brace_start = m.end() - 1
|
|
1018
|
+
depth = 0
|
|
1019
|
+
for idx in range(brace_start, len(src)):
|
|
1020
|
+
ch = src[idx]
|
|
1021
|
+
if ch == "{":
|
|
1022
|
+
depth += 1
|
|
1023
|
+
elif ch == "}":
|
|
1024
|
+
depth -= 1
|
|
1025
|
+
if depth == 0:
|
|
1026
|
+
return src[m.start() : idx + 1]
|
|
1027
|
+
return None
|
|
@@ -200,10 +200,6 @@ def _parse_structs_with_pycparser(cdef: str) -> dict[str, StructSpec]:
|
|
|
200
200
|
for decl in struct.decls:
|
|
201
201
|
ctype, is_ptr, is_const, arr_len = _ctype_from_decl(decl.type)
|
|
202
202
|
base = base_type_from_ctype(ctype)
|
|
203
|
-
try:
|
|
204
|
-
numpy_dtype_for_base_type(base)
|
|
205
|
-
except TypeError:
|
|
206
|
-
continue
|
|
207
203
|
fields.append(
|
|
208
204
|
StructField(
|
|
209
205
|
name=decl.name,
|
|
@@ -311,7 +307,7 @@ def _parse_structs_regex(cdef: str) -> dict[str, StructSpec]:
|
|
|
311
307
|
if not line:
|
|
312
308
|
continue
|
|
313
309
|
line = re.sub(r"/\*.*?\*/", "", line).strip()
|
|
314
|
-
m_field = re.match(r"(
|
|
310
|
+
m_field = re.match(r"(.+?\*?)\s*([A-Za-z_]\w*)(\s*\[(\d+)\])?$", line)
|
|
315
311
|
if not m_field:
|
|
316
312
|
continue
|
|
317
313
|
raw_ctype = m_field.group(1).strip()
|
|
@@ -320,10 +316,6 @@ def _parse_structs_regex(cdef: str) -> dict[str, StructSpec]:
|
|
|
320
316
|
is_pointer = "*" in raw_ctype
|
|
321
317
|
is_const = "const" in raw_ctype
|
|
322
318
|
base = base_type_from_ctype(raw_ctype)
|
|
323
|
-
try:
|
|
324
|
-
numpy_dtype_for_base_type(base)
|
|
325
|
-
except TypeError:
|
|
326
|
-
continue
|
|
327
319
|
fields.append(
|
|
328
320
|
StructField(
|
|
329
321
|
name=fname,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|