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
