Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package ocaml-pyml for openSUSE:Factory 
checked in at 2023-01-05 15:00:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ocaml-pyml (Old)
 and      /work/SRC/openSUSE:Factory/.ocaml-pyml.new.1563 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ocaml-pyml"

Thu Jan  5 15:00:57 2023 rev:6 rq:1056078 version:20220905

Changes:
--------
--- /work/SRC/openSUSE:Factory/ocaml-pyml/ocaml-pyml.changes    2022-07-01 
13:45:30.106947431 +0200
+++ /work/SRC/openSUSE:Factory/.ocaml-pyml.new.1563/ocaml-pyml.changes  
2023-01-05 15:01:18.193162710 +0100
@@ -1,0 +2,6 @@
+Sun Jan  1 01:01:01 UTC 2023 - [email protected]
+
+- Update to version 20220905
+  use _multibuild for testsuite
+
+-------------------------------------------------------------------

Old:
----
  ocaml-pyml-20220615.tar.xz

New:
----
  _multibuild
  ocaml-pyml-20220905.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ ocaml-pyml.spec ++++++
--- /var/tmp/diff_new_pack.C4SYdl/_old  2023-01-05 15:01:18.617164945 +0100
+++ /var/tmp/diff_new_pack.C4SYdl/_new  2023-01-05 15:01:18.621164966 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package ocaml-pyml
 #
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,28 +16,43 @@
 #
 
 
-Name:           ocaml-pyml
-Version:        20220615
+%bcond_with ocaml_pyml_testsuite
+%define build_flavor @BUILD_FLAVOR@%nil
+%if "%build_flavor" == "testsuite"
+%if %{without ocaml_pyml_testsuite}
+ExclusiveArch:  do-not-build
+%endif
+%define nsuffix -testsuite
+%else
+%define nsuffix %nil
+%endif
+
+%define     pkg ocaml-pyml
+Name:           %pkg%nsuffix
+Version:        20220905
 Release:        0
 %{?ocaml_preserve_bytecode}
 Summary:        Stdcompat: compatibility module for OCaml standard library
 License:        BSD-2-Clause
 Group:          Development/Languages/OCaml
 URL:            https://opam.ocaml.org/packages/pyml
-Source0:        %name-%version.tar.xz
+Source0:        %pkg-%version.tar.xz
 BuildRequires:  ocaml
 BuildRequires:  ocaml-dune >= 2.8
-BuildRequires:  ocaml-rpm-macros >= 20220409
+BuildRequires:  ocaml-rpm-macros >= 20230101
 BuildRequires:  ocamlfind(bigarray)
 BuildRequires:  ocamlfind(stdcompat)
 BuildRequires:  ocamlfind(unix)
-# make check
+
+%if "%build_flavor" == "testsuite"
+BuildRequires:  ocamlfind(pyml)
 %if 0%{?suse_version} > 1315
 BuildRequires:  python3-numpy
 %else
 BuildRequires:  python-numpy
 %endif
 BuildRequires:  which
+%endif
 
 %description
 Stdcompat is a compatibility layer allowing programs to use some recent 
additions to the OCaml standard library while preserving the ability to be 
compiled on former versions of OCaml.
@@ -53,22 +68,30 @@
 developing applications that use %name.
 
 %prep
-%autosetup -p1
+%autosetup -p1 -n %pkg-%version
 
 %build
 dune_release_pkgs='pyml'
 %ocaml_dune_setup
+%if "%build_flavor" == ""
 %ocaml_dune_build
+%endif
 
 %install
+%if "%build_flavor" == ""
 %ocaml_dune_install
 %ocaml_create_file_list
+%endif
 
+%if "%build_flavor" == "testsuite"
 %check
 %ocaml_dune_test
+%endif
 
+%if "%build_flavor" == ""
 %files -f %name.files
 
 %files devel -f %name.files.devel
+%endif
 
 %changelog

++++++ _multibuild ++++++
<multibuild>
  <package>testsuite</package>
</multibuild>

++++++ _service ++++++
--- /var/tmp/diff_new_pack.C4SYdl/_old  2023-01-05 15:01:18.697165366 +0100
+++ /var/tmp/diff_new_pack.C4SYdl/_new  2023-01-05 15:01:18.701165387 +0100
@@ -1,7 +1,7 @@
 <services>
   <service name="tar_scm" mode="disabled">
     <param name="filename">ocaml-pyml</param>
-    <param name="revision">c4bff7730364c4df8ca0213694e9bf512bd935fe</param>
+    <param name="revision">e33f4c49cc97e7bc6f8e5faaa64cce994470642e</param>
     <param name="scm">git</param>
     <param name="submodules">disable</param>
     <param name="url">https://github.com/thierry-martinez/pyml.git</param>

++++++ ocaml-pyml-20220615.tar.xz -> ocaml-pyml-20220905.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/CHANGES.md 
new/ocaml-pyml-20220905/CHANGES.md
--- old/ocaml-pyml-20220615/CHANGES.md  2022-06-15 14:58:57.000000000 +0200
+++ new/ocaml-pyml-20220905/CHANGES.md  2022-10-24 12:56:31.000000000 +0200
@@ -1,5 +1,55 @@
 [*] marks changes that break compatibility with previous versions.
 
+# Development version
+
+- Fix segmentation fault by forgetting objects on library unloading
+  (observed on Fedora Rawhide with address randomization)
+  (reported by Jerry James,
+  https://github.com/thierry-martinez/pyml/issues/85)
+
+# 2022-09-05
+
+- Support for OCaml 5.0
+
+- Support for Python 3.11.
+  All OCaml exceptions raised in callbacks are now encapsulated with their
+  backtrace in Python exceptions instead of bypassing the Python interpreter.
+  The former behavior led to segmentation faults in the test-suite, and nothing
+  indicate that previous versions of Python were supposed to support that well.
+  *The new behavior can break existing code*, especially code relying on
+  `Py.Run.simple_string`, which now catches all exceptions, including OCaml
+  exceptions. If you need proper exception handling, you can use `Py.Run.eval`.
+  (reported by Jerry James,
+  https://github.com/thierry-martinez/pyml/issues/84)
+
+- New function `Py.Object.dir`.
+
+- New functions `Py.Err.set_interrupt` and, for Python >=3.10,
+ `Py.Err.set_interrupt_ex`.
+
+- New functions
+  `Py.Dict.{to_bindings_seq, to_bindings_seq_map, to_bindings_string_seq}`.
+
+- New function `Py.Capsule.create`, equivalent to `Py.Capsule.make`, but
+  returning the record `{ wrap; unwrap }` of the new type `'a Py.Capsule.t`
+  instead of a pair.
+
+- Do not let `python` capture `sigint` by default (can be changed by passing
+  `~python_sigint:true` to `Py.initialize`): `Ctrl+C` now interrupts the
+  program, even after `Py.initialize` is called.
+  (reported by Arulselvan Madhavan,
+  https://github.com/thierry-martinez/pyml/issues/83)
+
+- Bindings for exceptions: `PyExc_EncodingWarning` (added in Python 3.10),
+  `PyExc_ResourceWarning` (added in Python 3.2)
+  (reported by Jerry James,
+  https://github.com/thierry-martinez/pyml/issues/84)
+
+- Fixes in bindings for `PyCompilerFlags`, `PyMarshal_WriteObjectToFile`,
+  and `PySet_Clear`
+  (reported by Jerry James,
+  https://github.com/thierry-martinez/pyml/issues/84)
+
 # 2022-06-15
 
 - `Numpy.to_bigarray_k` is continuation-passing-style version of 
`Numpy.to_bigarray`,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/Makefile 
new/ocaml-pyml-20220905/Makefile
--- old/ocaml-pyml-20220615/Makefile    2022-06-15 14:58:57.000000000 +0200
+++ new/ocaml-pyml-20220905/Makefile    2022-10-24 12:56:31.000000000 +0200
@@ -36,12 +36,6 @@
        OCAMLDEP := $(OCAMLFIND) ocamldep
        OCAMLDOC := $(OCAMLFIND) ocamldoc
        STDCOMPAT := $(shell $(OCAMLFIND) query stdcompat)
-        OCAMLCFLAGS := -package stdcompat
-        OCAMLLDFLAGS := -linkpkg
-       OCAMLBYTECODELIBS := -package unix,stdcompat
-       OCAMLBYTECODELIBSNUMPY := -package unix,stdcompat,bigarray
-       OCAMLNATIVELIBS := -package unix,stdcompat
-       OCAMLNATIVELIBSNUMPY := -package unix,stdcompat,bigarray
 else
        OCAMLC := $(shell \
                if ocamlc.opt -version >/dev/null 2>&1; then \
@@ -67,12 +61,41 @@
        OCAMLDEP := ocamldep
        OCAMLDOC := ocamldoc
        STDCOMPAT := .
-        OCAMLCFLAGS := -I $(STDCOMPAT)
-        OCAMLLDFLAGS := -I $(STDCOMPAT)
-       OCAMLBYTECODELIBS := unix.cma stdcompat.cma
-       OCAMLBYTECODELIBSNUMPY := unix.cma stdcompat.cma bigarray.cma
-       OCAMLNATIVELIBS := unix.cmxa stdcompat.cmxa
-       OCAMLNATIVELIBSNUMPY := unix.cmxa stdcompat.cmxa bigarray.cmxa
+endif
+
+OCAMLVERSION := $(shell $(OCAMLC) -version)
+OCAMLVERSION_LIST := $(subst ., ,$(OCAMLVERSION))
+OCAMLVERSION_MAJOR := $(word 1,$(OCAMLVERSION_LIST))
+
+LIBRARIES := unix stdcompat
+
+ifeq ($(OCAMLVERSION_MAJOR),5)
+       LIBRARIES_NUMPY = $(LIBRARIES)
+       OCAMLCFLAGS = -I +unix
+else
+       LIBRARIES_NUMPY = $(LIBRARIES) bigarray
+endif
+
+null  :=
+space := $(null) #
+comma := ,
+
+ifneq ($(HAVE_OCAMLFIND),no)
+        OCAMLCFLAGS += -package stdcompat
+        OCAMLLDFLAGS += -linkpkg
+       PACKAGES := $(subst $(space),$(comma),$(LIBRARIES))
+       PACKAGES_NUMPY := $(subst $(space),$(comma),$(LIBRARIES_NUMPY))
+       OCAMLBYTECODELIBS := -package $(PACKAGES)
+       OCAMLBYTECODELIBSNUMPY := -package $(PACKAGES_NUMPY)
+       OCAMLNATIVELIBS := -package $(PACKAGES)
+       OCAMLNATIVELIBSNUMPY := -package $(PACKAGES_NUMPY)
+else
+        OCAMLCFLAGS += -I $(STDCOMPAT)
+        OCAMLLDFLAGS += -I $(STDCOMPAT)
+       OCAMLBYTECODELIBS := $(LIBRARIES:=.cma)
+       OCAMLBYTECODELIBSNUMPY := $(LIBRARIES_NUMPY:=.cma)
+       OCAMLNATIVELIBS := $(LIBRARIES:=.cmxa)
+       OCAMLNATIVELIBSNUMPY := $(LIBRARIES_NUMPY:=.cmxa)
 endif
 
 ifeq ($(wildcard $(STDCOMPAT)/stdcompat.cma),)
@@ -262,7 +285,7 @@
 pyml_wrappers.inc : pywrappers.ml
 
 pywrappers.mli : pywrappers.ml pytypes.cmi pyml_arch.cmi
-       $(OCAMLC) -i $< >$@
+       $(OCAMLC) $(OCAMLCFLAGS) -i $< >$@
 
 pyml_tests.native : py.cmi pyml.cmxa pyml_tests_common.cmx pyml_tests.cmx
        $(OCAMLOPT) $(OCAMLLDFLAGS) $(OCAMLNATIVELIBS) pyml.cmxa \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/README.md 
new/ocaml-pyml-20220905/README.md
--- old/ocaml-pyml-20220615/README.md   2022-06-15 14:58:57.000000000 +0200
+++ new/ocaml-pyml-20220905/README.md   2022-10-24 12:56:31.000000000 +0200
@@ -142,12 +142,15 @@
 where ``type`` belongs to the enumeration ``Py.Err.t``
 and ``msg`` is an OCaml string.
 If ``f`` raises an exception that is neither
-of the form ``Py.E`` nor ``Py.Err``, then the Python interpreter is 
interrupted,
-and the exception is raised back in OCaml.
+of the form ``Py.E`` nor ``Py.Err``, then
+this exception is encapsulated with its backtrace in a Python exception
+(of class `ocaml exception` derived from `BaseException` and not from
+`Exception`, so as not to be caught), leading the Python interpreter to
+be interrupted, and the exception is raised back in OCaml.
 
-``Py.Run.simple_string`` catches all Python exceptions and returns a single
-Boolean to indicate success. One can prefer ``Py.Run.eval`` to get proper
-error handling.
+``Py.Run.simple_string`` catches all Python exceptions (and OCaml
+exceptions as well) and returns a single Boolean to indicate
+success. One can prefer ``Py.Run.eval`` to get proper error handling.
 
 Data types
 ----------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/dune new/ocaml-pyml-20220905/dune
--- old/ocaml-pyml-20220615/dune        2022-06-15 14:58:57.000000000 +0200
+++ new/ocaml-pyml-20220905/dune        2022-10-24 12:56:31.000000000 +0200
@@ -3,7 +3,7 @@
   (modules numpy py pyops pycaml pyml_arch pytypes pywrappers pyutils)
   (foreign_stubs (language c) (names numpy_stubs pyml_stubs))
   (wrapped false)
-  (libraries bigarray stdcompat))
+  (libraries unix bigarray stdcompat))
 
 (executables
   (names generate)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/generate.ml 
new/ocaml-pyml-20220905/generate.ml
--- old/ocaml-pyml-20220615/generate.ml 2022-06-15 14:58:57.000000000 +0200
+++ new/ocaml-pyml-20220905/generate.ml 2022-10-24 12:56:31.000000000 +0200
@@ -147,6 +147,14 @@
      arguments = Fun [Int];
      result = Unit;
      optional = false; };
+   { symbol = "PyErr_SetInterrupt";
+     arguments = Fun [];
+     result = Unit;
+     optional = false; };
+   { symbol = "PyErr_SetInterruptEx";
+     arguments = Fun [Int];
+     result = Unit;
+     optional = true; }; (* since 3.10 *)
    { symbol = "PyErr_SetNone";
      arguments = Fun [PyObject false];
      result = Unit;
@@ -211,6 +219,10 @@
      arguments = Deref;
      result = PyObject false;
      optional = false; };
+   { symbol = "PyExc_EncodingWarning";
+     arguments = Deref;
+     result = PyObject false;
+     optional = true; }; (* Added in python 3.10 *)
    { symbol = "PyExc_EOFError";
      arguments = Deref;
      result = PyObject false;
@@ -251,6 +263,10 @@
      arguments = Deref;
      result = PyObject false;
      optional = false; };
+   { symbol = "PyExc_ResourceWarning";
+     arguments = Deref;
+     result = PyObject false;
+     optional = true; }; (* Added in python 3.2 *)
    { symbol = "PyExc_RuntimeError";
      arguments = Deref;
      result = PyObject false;
@@ -506,7 +522,7 @@
      optional = false; };
    { symbol = "PyMarshal_WriteObjectToFile";
      arguments = Fun [PyObject false; FileOut true; Int];
-     result = Int;
+     result = Unit;
      optional = false; };
    { symbol = "PyMarshal_WriteObjectToString";
      arguments = Fun [PyObject false; Int];
@@ -692,6 +708,10 @@
      arguments = Fun [PyObject false; String];
      result = Int;
      optional = false; };
+   { symbol = "PyObject_Dir";
+     arguments = Fun [PyObject false];
+     result = PyObject true;
+     optional = false; };
    { symbol = "PyObject_GetAttr";
      arguments = Fun [PyObject false; PyObject false];
      result = PyObject true;
@@ -911,7 +931,7 @@
      optional = false; };
    { symbol = "PySet_Clear";
      arguments = Fun [PyObject false];
-     result = Unit;
+     result = Int;
      optional = false; };
    { symbol = "PySet_Discard";
      arguments = Fun [PyObject false; PyObject false];
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/numpy_stubs.c 
new/ocaml-pyml-20220905/numpy_stubs.c
--- old/ocaml-pyml-20220615/numpy_stubs.c       2022-06-15 14:58:57.000000000 
+0200
+++ new/ocaml-pyml-20220905/numpy_stubs.c       2022-10-24 12:56:31.000000000 
+0200
@@ -188,7 +188,7 @@
     bigarray = caml_ba_alloc(kind | layout, nd, data, dims);
     free(dims);
     Py_INCREF(array);
-    struct custom_operations *oldops = Custom_ops_val(bigarray);
+    const struct custom_operations *oldops = Custom_ops_val(bigarray);
     struct numpy_custom_operations *newops = (struct numpy_custom_operations *)
         malloc(sizeof(struct numpy_custom_operations));
     newops->ops.identifier = oldops->identifier;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/py.ml 
new/ocaml-pyml-20220905/py.ml
--- old/ocaml-pyml-20220615/py.ml       2022-06-15 14:58:57.000000000 +0200
+++ new/ocaml-pyml-20220905/py.ml       2022-10-24 12:56:31.000000000 +0200
@@ -641,79 +641,97 @@
   | Pyml_arch.Windows -> ";"
   | _ -> ":"
 
-let initialize ?library_name ?interpreter ?version ?minor ?(verbose = false)
-    ?debug_build () =
+(* Preserve signal behavior for sigint (Ctrl+C)
+   (Reported by Arulselvan Madhavan,
+    see https://github.com/thierry-martinez/pyml/issues/83)
+
+   pythonlib changes the handling of sigint, making programs
+   uninterruptible when the library is loaded.
+
+   The following function restores sigint handling and `initialize`
+   uses it except if ~python_sigint:true is passed.
+ *)
+
+let keep_sigint f =
+  let previous_signal_behavior = Sys.signal Sys.sigint Sys.Signal_ignore in
+  Sys.set_signal Sys.sigint previous_signal_behavior;
+  Stdcompat.Fun.protect f
+    ~finally:(fun () -> Sys.set_signal Sys.sigint previous_signal_behavior)
+
+let initialize ?library_name ?interpreter ?version
+    ?minor ?(verbose = false) ?debug_build ?(python_sigint = false) () =
   if !initialized then
     failwith "Py.initialize: already initialized";
-  begin
+  let do_initialize () =
     match library_name with
     | Some library_name ->
         load_library (Some library_name);
     | None ->
-  begin
-    try
-      let python_full_path = find_interpreter interpreter version minor in
-      let interpreter_pythonpaths =
-        match python_full_path with
-          None -> []
-        | Some python_full_path' ->
-            pythonpaths_from_interpreter python_full_path' in
-      let new_pythonpaths =
-        List.rev_append !pythonpaths interpreter_pythonpaths in
-      if new_pythonpaths <> [] then
-        begin
-          let former_pythonpath = Sys.getenv_opt "PYTHONPATH" in
-          has_set_pythonpath := Some former_pythonpath;
-          let all_paths =
-            match former_pythonpath with
-              None -> new_pythonpaths
-            | Some former_pythonpath' ->
-                former_pythonpath' :: new_pythonpaths in
-          let pythonpath = String.concat path_separator all_paths in
-          if verbose then
-            begin
-              Printf.eprintf "Temporary set PYTHONPATH=\"%s\".\n" pythonpath;
-              flush stderr;
-            end;
-          Unix.putenv "PYTHONPATH" pythonpath
-      end;
-      let (version_major, version_minor) =
-        match python_full_path with
-          Some python_full_path' ->
-            let version_string =
-              python_version_from_interpreter python_full_path' in
-            let (version_major, version_minor) =
-              extract_version_major_minor version_string in
+        try
+          let python_full_path = find_interpreter interpreter version minor in
+          let interpreter_pythonpaths =
+            match python_full_path with
+              None -> []
+            | Some python_full_path' ->
+                pythonpaths_from_interpreter python_full_path' in
+          let new_pythonpaths =
+            List.rev_append !pythonpaths interpreter_pythonpaths in
+          if new_pythonpaths <> [] then
             begin
-              match version with
-                None -> ()
-              | Some version_major' ->
-                  if version_major <> version_major' then
-                    failwith
-                      (version_mismatch
-                         python_full_path' (string_of_int version_major)
-                         (string_of_int version_major'));
-                  match minor with
+              let former_pythonpath = Sys.getenv_opt "PYTHONPATH" in
+              has_set_pythonpath := Some former_pythonpath;
+              let all_paths =
+                match former_pythonpath with
+                  None -> new_pythonpaths
+                | Some former_pythonpath' ->
+                    former_pythonpath' :: new_pythonpaths in
+              let pythonpath = String.concat path_separator all_paths in
+              if verbose then
+                begin
+                  Printf.eprintf "Temporary set PYTHONPATH=\"%s\".\n" 
pythonpath;
+                  flush stderr;
+                end;
+              Unix.putenv "PYTHONPATH" pythonpath
+          end;
+          let (version_major, version_minor) =
+            match python_full_path with
+              Some python_full_path' ->
+                let version_string =
+                  python_version_from_interpreter python_full_path' in
+                let (version_major, version_minor) =
+                  extract_version_major_minor version_string in
+                begin
+                  match version with
                     None -> ()
-                  | Some version_minor' ->
-                      if version_minor <> version_minor' then
-                        let expected =
-                          build_version_string version_major version_minor in
-                        let got =
-                          build_version_string version_major' version_minor' in
+                  | Some version_major' ->
+                      if version_major <> version_major' then
                         failwith
-                          (version_mismatch python_full_path' expected got);
-            end;
-            (Some version_major, Some version_minor)
-        | _ -> version, minor in
-      initialize_library ~verbose ~version_major ~version_minor ~debug_build
-        python_full_path;
-    with e ->
-      uninit_pythonhome ();
-      uninit_pythonpath ();
-      raise e
-  end;
-  end;
+                          (version_mismatch
+                             python_full_path' (string_of_int version_major)
+                             (string_of_int version_major'));
+                      match minor with
+                        None -> ()
+                      | Some version_minor' ->
+                          if version_minor <> version_minor' then
+                            let expected =
+                              build_version_string version_major version_minor 
in
+                            let got =
+                              build_version_string version_major' 
version_minor' in
+                            failwith
+                              (version_mismatch python_full_path' expected 
got);
+                end;
+                (Some version_major, Some version_minor)
+            | _ -> version, minor in
+          initialize_library ~verbose ~version_major ~version_minor 
~debug_build
+            python_full_path;
+        with e ->
+          uninit_pythonhome ();
+          uninit_pythonpath ();
+          raise e in
+  if python_sigint then
+    do_initialize ()
+  else
+    keep_sigint do_initialize;
   let version = get_version () in
   let (version_major, version_minor) =
     extract_version_major_minor version in
@@ -747,6 +765,10 @@
   assert_initialized ();
   !version_minor_value
 
+let version_pair () =
+  assert_initialized ();
+  (!version_major_value, !version_minor_value)
+
 let null =
   pynull ()
 
@@ -761,12 +783,39 @@
 
 exception E of pyobject * pyobject
 
-let fetched_exception = ref None
+let create_ref_to_python_object () =
+  let result = ref None in
+  on_finalize (fun () -> result := None);
+  result
+
+let fetched_exception = create_ref_to_python_object ()
+
+let ocaml_exception_class = create_ref_to_python_object ()
+
+let ocaml_exception_capsule = create_ref_to_python_object ()
 
 let python_exception () =
   let ptype, pvalue, ptraceback = pyerr_fetch_internal () in
-  fetched_exception := Some (ptype, pvalue, ptraceback);
-  raise (E (ptype, pvalue))
+  if
+    match !ocaml_exception_class with
+    | None -> false
+    | Some ocaml_exception_class ->
+        Lazy.is_val ocaml_exception_class &&
+        Lazy.force ocaml_exception_class = ptype
+  then
+    begin
+      let args = Pywrappers.pyobject_getattrstring pvalue "args" in
+      assert (args <> null);
+      let capsule = Pywrappers.pysequence_getitem args 0 in
+      assert (capsule <> null);
+      let exc, bt = snd (Option.get !ocaml_exception_capsule) capsule in
+      Printexc.raise_with_backtrace exc bt
+    end
+  else
+    begin
+      fetched_exception := Some (ptype, pvalue, ptraceback);
+      raise (E (ptype, pvalue))
+    end
 
 let check_not_null result =
   if result = null then
@@ -952,6 +1001,10 @@
   let of_bindings = of_bindings_map id id
 
   let of_bindings_string = of_bindings_map String_.of_string id
+
+  let set_item_string dict name value =
+    assert_not_null "set_item_string" dict;
+    assert_int_success (Pywrappers.pydict_setitemstring dict name value)
 end
 
 module Object_ = struct
@@ -1042,6 +1095,11 @@
 end
 
 module Capsule = struct
+  type 'a t = {
+    wrap : 'a -> pyobject;
+    unwrap : pyobject -> 'a;
+  }
+
   let is_valid v name  = pycapsule_isvalid v name <> 0
 
   let check v = is_valid v "ocaml-capsule"
@@ -1075,6 +1133,10 @@
         v in
       (wrap, unwrap)
 
+  let create name =
+    let wrap, unwrap = make name in
+    { wrap; unwrap }
+
   let type_of x =
     if pycapsule_check x = 0 then
       Type.mismatch "capsule" x;
@@ -1437,6 +1499,14 @@
 
   let set_error error msg =
     set_object (of_error error) (String.of_string msg)
+
+  let set_interrupt () =
+    Pywrappers.pyerr_setinterrupt ()
+
+  let set_interrupt_ex signal =
+    if version_pair () < (3, 10) then
+      failwith "set_interrupt_ex: only available with Python >= 3.10";
+    Pywrappers.pyerr_setinterruptex signal
 end
 
 exception Err of Err.t * string
@@ -1606,6 +1676,10 @@
   let size obj =
     assert_not_null "size" obj;
     check_int (Pywrappers.pyobject_size obj)
+
+  let dir obj =
+    assert_not_null "dir" obj;
+    check_not_null (Pywrappers.pyobject_dir obj)
 end
 
 let exception_printer exn =
@@ -1898,6 +1972,14 @@
     match next i with
       None -> false
     | Some item -> p item || exists p i
+
+  let unsafe_to_seq_map f i =
+    let rec seq () =
+      match next i with
+      | None -> Seq.Nil
+      | Some item ->
+          Seq.Cons (f item, seq) in
+    seq
 end
 
 (* From stdcompat *)
@@ -2110,10 +2192,6 @@
 
   let items dict = check_not_null (Pywrappers.pydict_items dict)
 
-  let set_item_string dict name value =
-    assert_not_null "set_item_string" dict;
-    assert_int_success (Pywrappers.pydict_setitemstring dict name value)
-
   let size dict =
     let sz = Pywrappers.pydict_size dict in
     assert_int_success sz;
@@ -2146,6 +2224,17 @@
       p key value
     end (Object.get_iter (items dict))
 
+  let to_bindings_seq_map fkey fvalue dict =
+    Iter_.unsafe_to_seq_map
+      (fun pair ->
+        let (key, value) = Tuple.to_pair pair in
+        (fkey key, fvalue value))
+      (Object.get_iter (items dict))
+
+  let to_bindings_seq = to_bindings_seq_map id id
+
+  let to_bindings_string_seq = to_bindings_seq_map String.to_string id
+
   let to_bindings_map fkey fvalue dict =
     Iter_.to_list_map begin fun pair ->
       let (key, value) = Tuple.to_pair pair in
@@ -2171,7 +2260,7 @@
 
   let clear o =
     assert_not_null "clear" o;
-    Pywrappers.pyset_clear o
+    assert_int_success (Pywrappers.pyset_clear o)
 
   let copy v = check_not_null (Pywrappers.pyset_new v)
 
@@ -2241,6 +2330,34 @@
 
 exception Err_with_traceback of Err.t * string * Traceback.t
 
+module Class = struct
+  let init ?(parents = []) ?(fields = []) ?(methods = []) classname =
+    if version_major () >= 3 then
+      let methods = List.rev_map (fun (name, closure) ->
+        (name, Pywrappers.Python3.pyinstancemethod_new closure)) methods in
+      Type.create classname parents (List.rev_append methods fields)
+    else
+      let classname = String.of_string classname in
+      let dict = Dict_.of_bindings_string fields in
+      let c =
+        check_not_null (Pywrappers.Python2.pyclass_new (Tuple_.of_list parents)
+          dict classname) in
+      let add_method (name, closure) =
+        let m = check_not_null (Pywrappers.pymethod_new closure null c) in
+        Dict_.set_item_string dict name m in
+      List.iter add_method methods;
+      c
+end
+
+let () =
+  ocaml_exception_class :=
+    Some (lazy (Class.init ~parents:[Pywrappers.pyexc_baseexception ()]
+      "ocaml exception"))
+
+let () =
+  ocaml_exception_capsule :=
+    Some (Capsule.make "ocaml_exception_capsule")
+
 module Callable = struct
   let check v = Pywrappers.pycallable_check v <> 0
 
@@ -2264,6 +2381,12 @@
             Err.restore (Err.of_error errtype) (String.of_string msg) 
traceback;
         in
         null
+    | e ->
+        let err =
+          fst (Option.get !ocaml_exception_capsule)
+            (e, Printexc.get_raw_backtrace ()) in
+        Err.set_object (Lazy.force (Option.get !ocaml_exception_class)) err;
+        null
 
   let of_function_as_tuple ?name ?(docstring = "Anonymous closure") f =
     check_not_null (pywrap_closure name docstring
@@ -2449,25 +2572,6 @@
     |> assert_int_success
 end
 
-module Class = struct
-  let init ?(parents = []) ?(fields = []) ?(methods = []) classname =
-    if version_major () >= 3 then
-      let methods = List.rev_map (fun (name, closure) ->
-        (name, Pywrappers.Python3.pyinstancemethod_new closure)) methods in
-      Type.create classname parents (List.rev_append methods fields)
-    else
-      let classname = String.of_string classname in
-      let dict = Dict.of_bindings_string fields in
-      let c =
-        check_not_null (Pywrappers.Python2.pyclass_new (Tuple.of_list parents)
-          dict classname) in
-      let add_method (name, closure) =
-        let m = check_not_null (Pywrappers.pymethod_new closure null c) in
-        Dict.set_item_string dict name m in
-      List.iter add_method methods;
-      c
-end
-
 module Iter = struct
   include Iter_
 
@@ -2531,14 +2635,6 @@
           Seq.Cons (item, seq) in
     seq
 
-  let unsafe_to_seq_map f i =
-    let rec seq () =
-      match next i with
-      | None -> Seq.Nil
-      | Some item ->
-          Seq.Cons (f item, seq) in
-    seq
-
   let of_list l =
     let l = ref l in
     let next () =
@@ -2648,7 +2744,7 @@
 
   let write_object_to_file v file version =
     let fd = Pytypes.file_map Unix.descr_of_out_channel file in
-    assert_int_success (Pywrappers.pymarshal_writeobjecttofile v fd version)
+    Pywrappers.pymarshal_writeobjecttofile v fd version
 
   let dump ?(version = version ()) v file =
     write_object_to_file v file version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/py.mli 
new/ocaml-pyml-20220905/py.mli
--- old/ocaml-pyml-20220615/py.mli      2022-06-15 14:58:57.000000000 +0200
+++ new/ocaml-pyml-20220905/py.mli      2022-10-24 12:56:31.000000000 +0200
@@ -3,7 +3,8 @@
 (** Call [initialize ()] first. *)
 
 val initialize: ?library_name:string -> ?interpreter:string -> ?version:int ->
-  ?minor:int -> ?verbose:bool -> ?debug_build:bool -> unit -> unit
+  ?minor:int -> ?verbose:bool -> ?debug_build:bool -> ?python_sigint:bool ->
+  unit -> unit
 (** [initialize ~interpreter ~version ~minor ~verbose ~debug_build ()] finds
     and loads the Python library.
     This function should be called before any other functions, except
@@ -23,11 +24,17 @@
     in the directory [../lib] relatively to the directory where the
     [python] executable is. If the library has been statically linked
     with the executable, it will be used.
-    When [verbose] is true (default: false), library filenames that are
+    When [verbose] is [true] (default: [false]), library filenames that are
     tried to be loaded are printed on standard error.
     [debug_build] specifies whether the Python library is a debug build:
     if the argument is left unspecified, debug build is detected
-    automatically. *)
+    automatically.
+    If [python_sigint] is [true] (default: [false]), the function let
+    [pythonlib] take handle on [sigint], preventing programs from
+    being interrupted by [Ctrl+C]. When [python_sigint] is [false]
+    (the default), the previous signal behavior of [sigint] is restored after
+    the library has been loaded (so, [Ctrl+C] will still interrupt the
+    program, unless this behavior was changed elsewhere). *)
 
 val finalize: unit -> unit
 (** [finalize ()] unloads the library. No other functions except
@@ -62,6 +69,10 @@
 (** [version_minor ()] returns the minor number (the second component) of the
     version of the Python library. *)
 
+val version_pair: unit -> int * int
+(** [version_pair ()] returns the major and the minor numbers of the
+    version of the Python library. *)
+
 type compare = Pytypes.compare = LT | LE | EQ | NE | GT | GE
 
 (** Either a filename or a channel.
@@ -286,6 +297,10 @@
   val size: t -> int
   (** Wrapper for
       {{:https://docs.python.org/3/c-api/object.html#c.PyObject_Size} 
PyObject_Size} *)
+
+  val dir: t -> t
+  (** Wrapper for
+      {{:https://docs.python.org/3/c-api/object.html#c.PyObject_Dir} 
PyObject_Dir} *)
 end
 
 exception E of Object.t * Object.t
@@ -475,11 +490,16 @@
 
 (** Embedding of OCaml values in Python. *)
 module Capsule: sig
+  type 'a t = {
+    wrap : 'a -> Object.t;
+    unwrap : Object.t -> 'a;
+  }
+
   val check: Object.t -> bool
   (** [check v] returns [true] if [v] contains an OCaml value. *)
 
-  val make: string -> ('a -> Object.t) * (Object.t -> 'a)
-  (** For a given type ['a], [make s] returns a pair [(wrap, unwrap)].
+  val create: string -> 'a t
+  (** For a given type ['a], [create s] returns a pair [{ wrap; unwrap }].
       [wrap v] transforms the value [v] of type 'a to an opaque Python object.
       [unwrap w] transforms the opaque Python object [w] previously obtained
       with [wrap v] into the original OCaml value [v],
@@ -487,6 +507,9 @@
       [Failure _] is raised if a wrapper has already been generated for a type
       of the same name. *)
 
+  val make: string -> ('a -> Object.t) * (Object.t -> 'a)
+  (** Same as {!val:create}, but returns a plain pair instead of a record. *)
+
   val type_of: Object.t -> string
   (** [type_of w] returns the type string associated to the opaque Python
       object [w]. *)
@@ -688,8 +711,8 @@
       [p key value]. *)
 
   val to_bindings: Object.t -> (Object.t * Object.t) list
-  (** [to_bindings o] returns all the pairs [(key, value)] in the Python 
dictionary
-      [o]. *)
+  (** [to_bindings o] returns all the pairs [(key, value)] in the Python
+      dictionary [o]. *)
 
   val to_bindings_map: (Object.t -> 'a) -> (Object.t -> 'b) -> Object.t ->
     ('a * 'b) list
@@ -700,6 +723,19 @@
   (** [to_bindings_string o] returns all the pairs [(key, value)] in the Python
       dictionary [o]. *)
 
+  val to_bindings_seq: Object.t -> (Object.t * Object.t) Stdcompat.Seq.t
+  (** [to_bindings_seq o] returns the ephemeral sequence of all the pairs
+      (key, value) in the Python dictionary [o]. *)
+
+  val to_bindings_seq_map: (Object.t -> 'a) -> (Object.t -> 'b) -> Object.t ->
+    ('a * 'b) Stdcompat.Seq.t
+  (** [to_bindings_seq_map fkey fvalue o] returns the ephemeral sequence of all
+      the pairs (fkey key, fvalue value) in the Python dictionary [o]. *)
+
+  val to_bindings_string_seq: Object.t -> (string * Object.t) Stdcompat.Seq.t
+  (** [to_bindings_string_seq o] returns the ephemeral sequence of all the 
pairs
+      (key, value) in the Python dictionary [o]. *)
+
   val of_bindings: (Object.t * Object.t) list -> Object.t
   (** [of_bindings b] returns then Python dictionary mapping all the pairs
       [(key, value)] in [b]. *)
@@ -866,6 +902,14 @@
       {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetObject} 
PyErr_SetObject}.
       In a closure/method/callback, it is recommended to raise a [Py.E _] 
exception
       instead. *)
+
+  val set_interrupt: unit -> unit
+  (** Wrapper for
+      {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetInterrupt} 
PyErr_SetInterrupt} *)
+
+  val set_interrupt_ex: int -> unit
+  (** Since Python 3.10. Wrapper for
+      
{{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetInterruptEx} 
PyErr_SetInterruptEx} *)
 end
 
 module Traceback : sig
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/pyml.opam 
new/ocaml-pyml-20220905/pyml.opam
--- old/ocaml-pyml-20220615/pyml.opam   2022-06-15 14:58:57.000000000 +0200
+++ new/ocaml-pyml-20220905/pyml.opam   2022-10-24 12:56:31.000000000 +0200
@@ -32,4 +32,3 @@
   ]
 ]
 dev-repo: "git+https://github.com/thierry-martinez/pyml.git";
-version: "20220615"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/pyml_stubs.c 
new/ocaml-pyml-20220905/pyml_stubs.c
--- old/ocaml-pyml-20220615/pyml_stubs.c        2022-06-15 14:58:57.000000000 
+0200
+++ new/ocaml-pyml-20220905/pyml_stubs.c        2022-10-24 12:56:31.000000000 
+0200
@@ -483,6 +483,10 @@
     if (Is_block(v)) {
         PyCompilerFlags *flags = malloc(sizeof(PyCompilerFlags));
         flags->cf_flags = Int_val(Field(Field(v, 0), 0));
+
+        /* only useful for Python >= 3.8 */
+        flags->cf_feature_version = version_minor;
+
         CAMLreturnT(PyCompilerFlags *, flags);
     }
     else {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/pyml_stubs.h 
new/ocaml-pyml-20220905/pyml_stubs.h
--- old/ocaml-pyml-20220615/pyml_stubs.h        2022-06-15 14:58:57.000000000 
+0200
+++ new/ocaml-pyml-20220905/pyml_stubs.h        2022-10-24 12:56:31.000000000 
+0200
@@ -229,6 +229,7 @@
 
 typedef struct {
     int cf_flags;
+    int cf_feature_version; /* Python >=3.8 */
 } PyCompilerFlags;
 
 #define Py_TPFLAGS_INT_SUBCLASS         (1L<<23)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/pyml_tests.ml 
new/ocaml-pyml-20220905/pyml_tests.ml
--- old/ocaml-pyml-20220615/pyml_tests.ml       2022-06-15 14:58:57.000000000 
+0200
+++ new/ocaml-pyml-20220905/pyml_tests.ml       2022-10-24 12:56:31.000000000 
+0200
@@ -187,7 +187,14 @@
     raise Exception('No exception raised')
 except Exception as err:
     if sys.version_info.major == 3 and sys.version_info.minor >= 7:
-        filenames = [f.filename for f in 
traceback.extract_tb(err.__traceback__)]
+        if sys.version_info.minor >= 11:
+            filenames = [
+                f.filename for f in
+                traceback.StackSummary.extract(
+                    traceback.walk_tb(err.__traceback__))]
+        else:
+            filenames = [
+                f.filename for f in traceback.extract_tb(err.__traceback__)]
         assert filenames == ['<string>', 'file2.ml', 'file1.ml']
     assert str(err) == \"Great\"
 ");
@@ -222,7 +229,7 @@
       let mywrap _ = raise Exit in
       Py.Module.set_function m "mywrap" mywrap;
       try
-        assert (Py.Run.simple_string "
+        ignore (Py.Run.eval ~start:File "
 from test import mywrap
 try:
     mywrap()
@@ -281,7 +288,7 @@
           Pyml_tests_common.Passed;
       with Py.E (_, value) ->
         Pyml_tests_common.Failed (Py.Object.to_string value))
-(*
+
 let () =
   Pyml_tests_common.add_test
     ~title:"reinitialize"
@@ -300,7 +307,7 @@
       Py.initialize ~verbose:true ?version ?minor ();
       Pyml_tests_common.Passed
     )
-*)
+
 let () =
   Pyml_tests_common.add_test
     ~title:"string conversion error"
@@ -359,7 +366,8 @@
 res = 0
 for v in ocaml_iterator: res += v
 ");
-      let res = Py.Dict.find_string (Py.Eval.get_globals ()) "res" in
+      let main = Py.Module.get_dict (Py.Import.add_module "__main__") in
+      let res = Py.Dict.find_string main "res" in
       assert (Py.Int.to_int res = 14);
       let iter = Py.Iter.of_list_map Py.String.of_string ["a"; "b"; "c"] in
       let list = Py.Iter.to_list_map Py.String.to_string iter in
@@ -389,7 +397,8 @@
 res = 0
 for v in ocaml_iterator2: res += v
 ");
-      let res = Py.Dict.find_string (Py.Eval.get_globals ()) "res" in
+      let main = Py.Module.get_dict (Py.Import.add_module "__main__") in
+      let res = Py.Dict.find_string main "res" in
       assert (Py.Int.to_int res = 14);
       let iter = iter_of_list Py.String.of_string ["a"; "b"; "c"] in
       let list = Py.Iter.to_list_map Py.String.to_string iter in
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/ocaml-pyml-20220615/pyml_tests_common.ml 
new/ocaml-pyml-20220905/pyml_tests_common.ml
--- old/ocaml-pyml-20220615/pyml_tests_common.ml        2022-06-15 
14:58:57.000000000 +0200
+++ new/ocaml-pyml-20220905/pyml_tests_common.ml        2022-10-24 
12:56:31.000000000 +0200
@@ -61,10 +61,11 @@
        begin
          match  String.length version with
            1 -> None, Some (int_of_string version), None
-         | 3 when version.[1] = '.' ->
+         | (3 | 4) when version.[1] = '.' ->
              None,
              Some (int_of_string (String.sub version 0 1)),
-             Some (int_of_string (String.sub version 2 1))
+             Some
+               (int_of_string (String.sub version 2 (String.length version - 
2)))
          | _ -> Some version, None, None
        end
     | _ -> failwith "Argument should be a version number" in

Reply via email to