tinycwrap 0.0.3__tar.gz → 0.0.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinycwrap
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: Lightweight C-to-Python wrapper generator using CFFI and NumPy
5
5
  Author: TinyCWrap Contributors
6
6
  Requires-Python: >=3.9
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tinycwrap"
7
- version = "0.0.3"
7
+ version = "0.0.4"
8
8
  description = "Lightweight C-to-Python wrapper generator using CFFI and NumPy"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -27,12 +27,18 @@ def test_circle_points_length_contract(cg):
27
27
  assert pts.shape == (101,)
28
28
 
29
29
 
30
+
30
31
  def test_return_struct_array_member(cg):
31
32
  seg = cg.G2DSegment()
32
33
  seg.type = 1
33
34
  seg.data = [0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0]
34
35
  assert seg.type == 1
35
36
  np.testing.assert_allclose(seg.data, [0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0])
37
+ seg= cg.G2DSegment(type=2, data=[9.0]*8)
38
+ assert seg.type == 2
39
+ np.testing.assert_allclose(seg.data, [9.0]*8)
40
+
41
+
36
42
 
37
43
  def test_geom2d_return_strct(cg):
38
44
  seg = cg.geom2d_line_segment_from_start_length(0.1, 0.2, 0.5, 0.4, 0.3)
@@ -39,3 +39,16 @@ def test_return_class_path(cp):
39
39
  seg = path.segments[0]
40
40
  assert seg['type'] == 1 # circle segment
41
41
  np.testing.assert_allclose(seg['data'][:3], [0.0, 0.0, 1.0]) # cx,cy,radius
42
+
43
+ def test_build_pointer_from_data(cp):
44
+ segments=cp.geom2d_rectangle_to_path(2.0, 3.0)
45
+ assert segments.shape == (4,)
46
+ path=cp.G2DPath(segments=segments, len_segments=len(segments))
47
+ assert path.len_segments == 4
48
+ assert path.segments.shape == (4,)
49
+
50
+ def test_repr_pointer_array(cp):
51
+ path = cp.G2DPath(len_segments=2)
52
+ repr_str = repr(path)
53
+ assert "segments=<segments* 0x" in repr_str
54
+ assert "len_segments=2" in repr_str
@@ -6,6 +6,6 @@ from .cmodule import CModule
6
6
  try:
7
7
  __version__ = version("tinycwrap")
8
8
  except PackageNotFoundError:
9
- __version__ = "0.0.3"
9
+ __version__ = "0.0.4"
10
10
 
11
11
  __all__ = ["CModule", "__version__"]
@@ -507,15 +507,51 @@ class CModule:
507
507
  children[f.name] = arr
508
508
  data[f.name] = arr.ctypes.data
509
509
  data[len_field] = llen
510
+ elif provided is not None:
511
+ arr = np.ascontiguousarray(provided)
512
+ children[f.name] = arr
513
+ data[f.name] = arr.ctypes.data
514
+ if len_field in dtype.names and len_field not in kwargs:
515
+ data[len_field] = len(arr)
516
+ elif target_dtype is not None and len_field in dtype.names:
517
+ llen = int(kwargs.get(len_field, 1))
518
+ arr = np.zeros(llen, dtype=target_dtype)
519
+ children[f.name] = arr
520
+ data[f.name] = arr.ctypes.data
521
+ data[len_field] = llen
510
522
  for k in dtype.names:
511
523
  if k in kwargs:
512
- data[k] = kwargs[k]
524
+ val = kwargs[k]
525
+ field_info = dtype.fields[k][0]
526
+ if field_info.shape != ():
527
+ arr = np.asarray(val, dtype=field_info.base)
528
+ if arr.shape != field_info.shape:
529
+ raise ValueError(f"Field {k} expects shape {field_info.shape}, got {arr.shape}")
530
+ np.copyto(data[k], arr)
531
+ else:
532
+ if k in children:
533
+ data[k] = children[k].ctypes.data
534
+ elif isinstance(val, np.ndarray):
535
+ if val.dtype.kind == "V" and val.shape != ():
536
+ arr = np.ascontiguousarray(val)
537
+ data[k] = arr.ctypes.data
538
+ if f"len_{k}" in dtype.names and f"len_{k}" not in kwargs:
539
+ data[f"len_{k}"] = len(arr)
540
+ else:
541
+ data[k] = val
542
+ else:
543
+ data[k] = val
513
544
  object.__setattr__(self, "_data", data)
514
545
  object.__setattr__(self, "_children", children)
515
546
 
516
547
  def __repr__(self):
517
548
  parts_list = []
549
+ pointer_lengths = getattr(self, "_pointer_lengths", {})
518
550
  for name in dtype.names:
551
+ if name in pointer_lengths:
552
+ ptr_val = int(self._data[name])
553
+ parts_list.append(f"{name}=<{name}* 0x{ptr_val:x}>")
554
+ continue
519
555
  val = self._data[name]
520
556
  if val.shape == ():
521
557
  parts_list.append(f"{name}={val.item()!r}")
@@ -533,6 +569,7 @@ class CModule:
533
569
  "zeros": staticmethod(
534
570
  lambda n, _dtype=dtype: np.zeros(n, dtype=_dtype)
535
571
  ),
572
+ "_pointer_lengths": {f.name: f"len_{f.name}" for f in spec.fields if f.is_pointer},
536
573
  }
537
574
 
538
575
  for fname in dtype.names:
@@ -632,20 +669,26 @@ class CModule:
632
669
  struct_names = set(self._struct_specs.keys())
633
670
  params: list[str] = []
634
671
  len_params: list[str] = []
672
+ optional_seen = False
635
673
  for a in fspec.args:
636
674
  if a.is_array_out and a.name.lower().startswith("out"):
637
675
  params.append(f"{a.name}=None")
676
+ optional_seen = True
638
677
  elif a.is_length_param:
639
678
  len_params.append(f"{a.name}=None")
679
+ optional_seen = True
640
680
  elif (
641
681
  a.is_pointer
642
682
  and a.base_type in struct_names
643
683
  and not a.is_const
644
- and a.name.lower().startswith("out")
645
684
  ):
646
685
  params.append(f"{a.name}=None")
686
+ optional_seen = True
647
687
  else:
648
- params.append(a.name)
688
+ if optional_seen:
689
+ params.append(f"{a.name}=None")
690
+ else:
691
+ params.append(a.name)
649
692
 
650
693
  params.extend(len_params)
651
694
  signature = ", ".join(params)
@@ -763,11 +806,10 @@ class CModule:
763
806
  if a.is_array_in:
764
807
  const_prefix = "const " if a.is_const else ""
765
808
  if a.base_type in struct_names:
766
- cls_expr = f"_struct_classes['{a.base_type}']"
767
809
  dtype_expr = f"_struct_dtypes['{a.base_type}']"
768
810
  pre_lines += [
769
- f" if isinstance({a.name}, {cls_expr}):",
770
- f" arr_{a.name} = {a.name}._data",
811
+ f" if hasattr({a.name}, '_data') and getattr({a.name}, '_data', None) is not None and getattr({a.name}, '_data').dtype == {dtype_expr}:",
812
+ f" arr_{a.name} = getattr({a.name}, '_data')",
771
813
  " else:",
772
814
  f" arr_{a.name} = np.ascontiguousarray({a.name}, dtype={dtype_expr})",
773
815
  f" ptr_{a.name} = _self._ffi.cast('{const_prefix}{a.base_type} *', _self._ffi.from_buffer(arr_{a.name}))",
@@ -872,11 +914,10 @@ class CModule:
872
914
  )
873
915
  elif a.is_pointer and a.base_type in struct_names:
874
916
  dtype_expr = f"_struct_dtypes['{a.base_type}']"
875
- cls_expr = f"_struct_classes['{a.base_type}']"
876
917
  if a.is_const:
877
918
  out_lines += [
878
- f" if isinstance({a.name}, {cls_expr}):",
879
- f" arr_{a.name} = {a.name}._data",
919
+ f" if hasattr({a.name}, '_data') and getattr({a.name}, '_data', None) is not None and getattr({a.name}, '_data').dtype == {dtype_expr}:",
920
+ f" arr_{a.name} = getattr({a.name}, '_data')",
880
921
  " else:",
881
922
  f" arr_{a.name} = np.ascontiguousarray({a.name}, dtype={dtype_expr})",
882
923
  f" ptr_{a.name} = _self._ffi.cast('{a.base_type} *', _self._ffi.from_buffer(arr_{a.name}))",
@@ -884,33 +925,18 @@ class CModule:
884
925
  else:
885
926
  out_lines += [
886
927
  f" if {a.name} is None:",
887
- ]
888
- if a.array_len is not None:
889
- out_lines += [
890
- f" arr_{a.name} = np.zeros({int(a.array_len)}, dtype={dtype_expr})"
891
- ]
892
- elif a.name in contract_map:
893
- expr_py = _expr_py(contract_map[a.name])
894
- out_lines += [
895
- f" arr_{a.name} = np.zeros(int({expr_py}), dtype={dtype_expr})"
896
- ]
897
- elif a.array_len is None and a.name not in contract_map:
898
- out_lines += [
899
- f" arr_{a.name} = np.zeros((), dtype={dtype_expr})"
900
- ]
901
- else:
902
- out_lines += [
903
- f" raise ValueError('{fspec.name}: provide {a.name} or a Contract for its length')"
904
- ]
905
- out_lines += [
906
- f" elif isinstance({a.name}, {cls_expr}):",
928
+ f" obj_{a.name} = _struct_classes['{a.base_type}']()",
929
+ f" arr_{a.name} = obj_{a.name}._data",
930
+ f" elif hasattr({a.name}, '_data') and getattr({a.name}, '_data', None) is not None and getattr({a.name}, '_data').dtype == {dtype_expr}:",
931
+ f" obj_{a.name} = {a.name}",
907
932
  f" arr_{a.name} = {a.name}._data",
908
933
  " else:",
909
934
  f" arr_{a.name} = np.ascontiguousarray({a.name}, dtype={dtype_expr})",
935
+ f" obj_{a.name} = None",
910
936
  f" ptr_{a.name} = _self._ffi.cast('{a.base_type} *', _self._ffi.from_buffer(arr_{a.name}))",
911
937
  ]
912
938
  output_names.append(a.name)
913
- output_vars.append(f"arr_{a.name}")
939
+ output_vars.append(f"obj_{a.name} if obj_{a.name} is not None else arr_{a.name}")
914
940
  arg_call_args[idx] = f"ptr_{a.name}"
915
941
  elif a.is_scalar and not a.is_length_param and arg_call_args[idx] is None:
916
942
  scalar_lines.append(f" {a.name} = {a.name}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinycwrap
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: Lightweight C-to-Python wrapper generator using CFFI and NumPy
5
5
  Author: TinyCWrap Contributors
6
6
  Requires-Python: >=3.9
File without changes
File without changes