tinycwrap 0.0.5__tar.gz → 0.0.6__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.5
3
+ Version: 0.0.6
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.5"
7
+ version = "0.0.6"
8
8
  description = "Lightweight C-to-Python wrapper generator using CFFI and NumPy"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -6,6 +6,6 @@ from .cmodule import CModule
6
6
  try:
7
7
  __version__ = version("tinycwrap")
8
8
  except PackageNotFoundError:
9
- __version__ = "0.0.5"
9
+ __version__ = "0.0.6"
10
10
 
11
11
  __all__ = ["CModule", "__version__"]
@@ -428,13 +428,14 @@ class CModule:
428
428
  "size_t",
429
429
  "ssize_t",
430
430
  )
431
- and (not arg.is_pointer)
432
431
  and arg.name in referenced
433
432
  ):
434
433
  arg.is_length_param = True
435
434
  for arg in fspec.args:
436
435
  if arg.is_length_param:
437
436
  arg.is_scalar = False
437
+ arg.is_array_in = False
438
+ arg.is_array_out = False
438
439
  elif (
439
440
  (not arg.is_pointer)
440
441
  and arg.array_len is None
@@ -753,6 +754,7 @@ class CModule:
753
754
  else:
754
755
  contract_map[target] = expr
755
756
 
757
+ length_pointer_names = {a.name for a in fspec.args if a.is_length_param and a.is_pointer}
756
758
  pointer_scalar_names = {
757
759
  a.name
758
760
  for a in fspec.args
@@ -773,7 +775,7 @@ class CModule:
773
775
  "ssize_t",
774
776
  )
775
777
  )
776
- }
778
+ } | length_pointer_names
777
779
 
778
780
  func_names = set(self._func_specs.keys())
779
781
 
@@ -782,6 +784,7 @@ class CModule:
782
784
  expr = re.sub(r"len\((\w+)\)", r"len(arr_\1)", expr)
783
785
  for name in pointer_scalar_names:
784
786
  expr = re.sub(rf"\b{name}\b", f"int(arr_{name}.ravel()[0])", expr)
787
+ expr = re.sub(rf"\b{name}\b", f"scalar_{name}", expr)
785
788
  if func_names:
786
789
  pattern = r"\b(" + "|".join(re.escape(n) for n in func_names) + r")\s*\(([^()]*)\)"
787
790
 
@@ -809,6 +812,17 @@ class CModule:
809
812
  scalar_lines: list[str] = []
810
813
 
811
814
  for a in fspec.args:
815
+ if a.is_length_param and a.is_pointer:
816
+ base_dtype = "np.dtype('int32')"
817
+ out_lines = [
818
+ f" arr_{a.name} = np.zeros((), dtype={base_dtype})",
819
+ f" ptr_{a.name} = _self._ffi.cast('{a.base_type} *', _self._ffi.from_buffer(arr_{a.name}))",
820
+ ]
821
+ call_args.append(f"ptr_{a.name}")
822
+ output_vars.append(f"arr_{a.name}")
823
+ output_names.append(a.name)
824
+ pointer_scalar_outputs.append((a.name, f"arr_{a.name}"))
825
+ continue
812
826
  if a.is_array_in:
813
827
  const_prefix = "const " if a.is_const else ""
814
828
  if a.base_type in struct_names:
@@ -840,21 +854,25 @@ class CModule:
840
854
  scalar_lines.append(f" {a.name} = {a.name}")
841
855
  call_args.append(a.name)
842
856
  elif a.is_length_param:
843
- if a.name in contract_map:
844
- expr_py = _expr_py(contract_map[a.name])
845
- length_lines += [
846
- f" if {a.name} is None:",
847
- f" {a.name} = int({expr_py})",
848
- f" else:",
849
- f" {a.name} = int({a.name})",
850
- ]
857
+ if a.is_pointer:
858
+ # handled in array/pointer branch
859
+ call_args.append(None)
851
860
  else:
852
- length_lines += [
853
- f" if {a.name} is None:",
854
- f" raise ValueError('{fspec.name}: length parameter {a.name} requires an explicit Contract')",
855
- f" {a.name} = int({a.name})",
856
- ]
857
- call_args.append(a.name)
861
+ if a.name in contract_map:
862
+ expr_py = _expr_py(contract_map[a.name])
863
+ length_lines += [
864
+ f" if {a.name} is None:",
865
+ f" {a.name} = int({expr_py})",
866
+ f" else:",
867
+ f" {a.name} = int({a.name})",
868
+ ]
869
+ else:
870
+ length_lines += [
871
+ f" if {a.name} is None:",
872
+ f" raise ValueError('{fspec.name}: length parameter {a.name} requires an explicit Contract')",
873
+ f" {a.name} = int({a.name})",
874
+ ]
875
+ call_args.append(a.name)
858
876
  else:
859
877
  # defer outputs / pointer handling
860
878
  call_args.append(None) # placeholder
@@ -960,11 +978,6 @@ class CModule:
960
978
  else:
961
979
  lines.append(f" res = {call_expr}")
962
980
 
963
- for out_name, out_var in zip(output_names, output_vars):
964
- if out_name in post_contract_map:
965
- expr_py = _expr_py(post_contract_map[out_name])
966
- lines.append(f" {out_var} = {out_var}[:int({expr_py})]")
967
-
968
981
  for name, arr_var in pointer_scalar_outputs:
969
982
  lines.append(f" scalar_{name} = int(np.asarray({arr_var}).ravel()[0])")
970
983
 
@@ -972,11 +985,19 @@ class CModule:
972
985
  for name, arr_var, cls_expr in struct_scalar_outputs:
973
986
  lines.append(f" obj_{name} = {cls_expr}()")
974
987
  lines.append(f" object.__setattr__(obj_{name}, '_data', np.array({arr_var}, copy=True))")
988
+ pointer_scalar_map = {arr: (name, f"scalar_{name}") for name, arr in pointer_scalar_outputs}
989
+ pairs = list(zip(output_names, output_vars))
990
+ # place array outputs before scalar pointer lengths
991
+ pairs_sorted = sorted(pairs, key=lambda p: 1 if p[1] in pointer_scalar_map else 0)
992
+ output_names_reordered: list[str] = []
975
993
  output_vars_final: list[str] = []
976
- for ov in output_vars:
994
+ for name, ov in pairs_sorted:
995
+ output_names_reordered.append(name)
977
996
  if ov in struct_scalar_map:
978
997
  n, _ = struct_scalar_map[ov]
979
998
  output_vars_final.append(f"obj_{n}")
999
+ elif ov in pointer_scalar_map:
1000
+ output_vars_final.append(pointer_scalar_map[ov][1])
980
1001
  else:
981
1002
  output_vars_final.append(ov)
982
1003
 
@@ -991,8 +1012,20 @@ class CModule:
991
1012
  ret_expr = f"{output_vars_final[0]}, res"
992
1013
  else:
993
1014
  ret_expr = "(" + ", ".join(output_vars_final) + "), res"
1015
+ # apply post-contract slicing using scalar lengths if available
1016
+ for out_name, out_var in zip(output_names_reordered, output_vars_final):
1017
+ if out_name in post_contract_map:
1018
+ expr_py = _expr_py(post_contract_map[out_name])
1019
+ scalar_names = {n for n, _ in pointer_scalar_outputs}
1020
+ if out_name in scalar_names:
1021
+ expr_py = expr_py.replace(out_name, f"scalar_{out_name}")
1022
+ ret_expr = ret_expr.replace(out_var, f"({out_var})[:int({expr_py})]")
994
1023
  if pointer_scalar_outputs:
995
- scalars = [f"scalar_{n}" for n, _ in pointer_scalar_outputs]
1024
+ scalars = [
1025
+ f"scalar_{n}"
1026
+ for n, _ in pointer_scalar_outputs
1027
+ if f"scalar_{n}" not in output_vars_final
1028
+ ]
996
1029
  if isinstance(ret_expr, str) and ret_expr.startswith("("):
997
1030
  ret_expr = (
998
1031
  ret_expr[:-1]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinycwrap
3
- Version: 0.0.5
3
+ Version: 0.0.6
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
File without changes
File without changes