diff -pruN 20211015-1/CHANGES.md 20220325-1/CHANGES.md
--- 20211015-1/CHANGES.md	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/CHANGES.md	2022-03-25 15:09:09.000000000 +0000
@@ -1,5 +1,43 @@
 [*] marks changes that break compatibility with previous versions.
 
+# 2022-03-25
+
+- Fix debug build detection
+
+- Expose the function `Py.Callable.handle_errors`
+
+# 2022-03-22
+
+- New function `Py.Import.exec_code_module_from_string`
+  (suggested by Francois Berenger, https://github.com/thierry-martinez/pyml/issues/78)
+
+- New function `Py.Module.compile` provides a better API than `Py.compile`.
+
+- `Py.Object.t` can now be serialized (with Marshal or output_value), using Python
+  pickle module
+
+- Cross-compiling friendly architecture detection
+  (suggested by @EduardoRFS, https://discuss.ocaml.org/t/a-zoo-of-values-for-system/8525/20)
+
+- Detect macro `unix` instead of `__linux__`, to handle *BSD OSes
+  (reported by Chris Pinnock, https://github.com/thierry-martinez/pyml/issues/74)
+
+- Fix bug in Windows
+
+- Null checks for many functions raising OCaml exceptions, instead of segmentation fault
+  (initial implementation by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/72)
+
+- Fix wide character conversion bugs leading to segmentation fault in Py_wfopen
+  (fixed by Jerry James, https://github.com/thierry-martinez/pyml/pull/75)
+
+- `Gc.full_major ()` before unloading `libpython` in `Py.finalize`, to prevent
+  segfaulting on finalizing dangling references to Python values after the library
+  had been unloaded
+  (reported by Denis Efremov on coccinelle mailing list)
+
+- Fix segmentation fault when `~debug_build:true` was passed to `Py.initialize`
+  (reported by Stéphane Glondu, https://github.com/thierry-martinez/pyml/issues/79)
+
 # 2021-10-15
 
 - More portable architecture detection
diff -pruN 20211015-1/debian/changelog 20220325-1/debian/changelog
--- 20211015-1/debian/changelog	2021-11-29 13:31:51.000000000 +0000
+++ 20220325-1/debian/changelog	2022-06-01 19:54:04.000000000 +0000
@@ -1,3 +1,20 @@
+pyml (20220325-1) unstable; urgency=medium
+
+  * Team upload
+  * New upstream release. This release fixes a bug with character conversion
+    (closes: #1009430).
+  * Remove patch 0002-Fix-79-segfault-when-debug_build-true-is-passed-to-i
+    which has been applied upstream.
+  * Standards-Version 4.6.1 (no change)
+
+ -- Ralf Treinen <treinen@debian.org>  Wed, 01 Jun 2022 21:54:04 +0200
+
+pyml (20211015-2) unstable; urgency=medium
+
+  * Fix segfault when ~debug_build:true is passed to initialize
+
+ -- Stéphane Glondu <glondu@debian.org>  Mon, 21 Mar 2022 17:20:22 +0100
+
 pyml (20211015-1) unstable; urgency=low
 
   [ Stéphane Glondu ]
diff -pruN 20211015-1/debian/control 20220325-1/debian/control
--- 20211015-1/debian/control	2021-11-29 13:31:51.000000000 +0000
+++ 20220325-1/debian/control	2022-06-01 19:54:04.000000000 +0000
@@ -5,12 +5,12 @@ Uploaders:
  Stéphane Glondu <glondu@debian.org>
 Build-Depends:
  debhelper-compat (= 13),
- ocaml-nox,
+ ocaml,
  ocaml-dune,
  libstdcompat-ocaml-dev (>= 13),
  python3-dev,
  dh-ocaml
-Standards-Version: 4.6.0
+Standards-Version: 4.6.1
 Rules-Requires-Root: no
 Section: ocaml
 Homepage: https://github.com/thierry-martinez/pyml
diff -pruN 20211015-1/dune 20220325-1/dune
--- 20211015-1/dune	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/dune	2022-03-25 15:09:09.000000000 +0000
@@ -16,14 +16,16 @@
   (action (run %{gen})))
 
 (rule
-  (target pyml_arch_generate.exe)
-  (deps pyml_arch_generate.c)
-  (action (run %{ocaml-config:native_c_compiler} -o pyml_arch_generate.exe pyml_arch_generate.c)))
+  (target pyml_arch.ml.sharp)
+  (deps pyml_arch.ml.c)
+  (action (with-stdout-to %{target}
+    (run %{ocaml-config:native_c_compiler} -E %{deps}))))
 
 (rule
   (target pyml_arch.ml)
-  (deps (:generate pyml_arch_generate.exe))
-  (action (run %{generate})))
+  (deps pyml_arch.ml.sharp)
+  (action (with-stdout-to %{target}
+    (run sed "/^#/d" %{deps}))))
 
 (library
   (name pyml_tests_common)
diff -pruN 20211015-1/dune-project 20220325-1/dune-project
--- 20211015-1/dune-project	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/dune-project	2022-03-25 15:09:09.000000000 +0000
@@ -24,10 +24,8 @@
  (synopsis "OCaml bindings for Python")
  (description "OCaml bindings for Python 2 and Python 3")
  (depends
-  (ocaml
-   (>= 3.12.1))
+  (ocaml (>= 3.12.1))
   (ocamlfind :build)
-  (stdcompat
-   (>= 17))
+  (stdcompat (>= 18))
   (conf-python-3-dev :with-test))
  (depopts utop))
diff -pruN 20211015-1/generate.ml 20220325-1/generate.ml
--- 20211015-1/generate.ml	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/generate.ml	2022-03-25 15:09:09.000000000 +0000
@@ -1572,7 +1572,7 @@ let coercion_of_c ty =
   | WideString -> Printf.sprintf "    CAMLreturn(pyml_wrap_wide_string(result));"
   | Int | Long | Size | Compare ->
       Printf.sprintf "    CAMLreturn(Val_int(result));"
-  | Int64 -> Printf.sprintf "    CAMLreturn(copy_int64(result));"
+  | Int64 -> Printf.sprintf "    CAMLreturn(caml_copy_int64(result));"
   | IntPtr -> Printf.sprintf "    CAMLreturn(pyml_wrap_intref(result));"
   | PyCompilerFlags ->
       Printf.sprintf "    CAMLreturn(pyml_wrap_compilerflags(result));"
diff -pruN 20211015-1/Makefile 20220325-1/Makefile
--- 20211015-1/Makefile	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/Makefile	2022-03-25 15:09:09.000000000 +0000
@@ -220,7 +220,7 @@ clean :
 	rm -f pyml.h
 	rm -f pyml_stubs.o dllpyml_stubs.so libpyml_stubs$(EXT_LIB)
 	rm -f numpy_stubs.o dllnumpy_stubs.so libnumpy_stubs$(EXT_LIB)
-	rm -f pyml_arch_generate.exe pyml_arch.ml
+	rm -f pyml_arch.ml
 	rm -f generate pyml_tests.native pyml_tests.bytecode
 	rm -f numpy_tests.native numpy_tests.bytecode
 	rm -f .depend
@@ -283,11 +283,8 @@ numpy_tests.bytecode : py.cmi pyml.cma n
 	$(OCAMLC) $(OCAMLLDFLAGS) $(OCAMLBYTECODELIBSNUMPY) pyml.cma \
 		numpy.cma pyml_tests_common.cmo numpy_tests.cmo -o $@
 
-pyml_arch_generate.exe : pyml_arch_generate.c
-	$(C_COMPILER) $< -o $@
-
-pyml_arch.ml : pyml_arch_generate.exe
-	./pyml_arch_generate.exe
+pyml_arch.ml : pyml_arch.ml.c
+	$(C_COMPILER) -E $< | sed '/^#/d' >$@
 
 pyml_arch.cmo pyml_arch.cmx : pyml_arch.cmi
 
diff -pruN 20211015-1/numpy_stubs.c 20220325-1/numpy_stubs.c
--- 20211015-1/numpy_stubs.c	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/numpy_stubs.c	2022-03-25 15:09:09.000000000 +0000
@@ -6,6 +6,17 @@
 #include <caml/custom.h>
 #include "pyml_stubs.h"
 
+value
+pyml_wrap(PyObject *object, bool steal);
+
+PyObject *
+pyml_unwrap(value v);
+
+struct numpy_custom_operations {
+    struct custom_operations ops;
+    PyObject *obj;
+};
+
 static void numpy_finalize(value v)
 {
     struct numpy_custom_operations *ops =
@@ -33,7 +44,7 @@ pyarray_of_bigarray_wrapper(
     }
     int type_num;
     intnat flags = Caml_ba_array_val(bigarray_ocaml)->flags;
-    switch (flags & BIGARRAY_KIND_MASK) {
+    switch (flags & CAML_BA_KIND_MASK) {
     case CAML_BA_FLOAT32:
         type_num = NPY_FLOAT;
         break;
@@ -59,7 +70,7 @@ pyarray_of_bigarray_wrapper(
         type_num = NPY_LONGLONG;
         break;
     case CAML_BA_CAML_INT:
-        failwith("Caml integers are unsupported for NumPy array");
+        caml_failwith("Caml integers are unsupported for NumPy array");
         break;
     case CAML_BA_NATIVE_INT:
         type_num = NPY_LONG;
@@ -76,7 +87,7 @@ pyarray_of_bigarray_wrapper(
         break;
 #endif
     default:
-        failwith("Unsupported bigarray kind for NumPy array");
+        caml_failwith("Unsupported bigarray kind for NumPy array");
     }
     int np_flags;
     switch (flags & CAML_BA_LAYOUT_MASK) {
@@ -87,7 +98,7 @@ pyarray_of_bigarray_wrapper(
         np_flags = NPY_ARRAY_FARRAY;
         break;
     default:
-        failwith("Unsupported bigarray layout for NumPy array");
+        caml_failwith("Unsupported bigarray layout for NumPy array");
     }
     void *data = Caml_ba_data_val(bigarray_ocaml);
     PyTypeObject (*PyArray_SubType) =
@@ -160,7 +171,7 @@ bigarray_of_pyarray_wrapper(
 #endif
         break;
     default:
-        failwith("Unsupported NumPy kind for bigarray");
+        caml_failwith("Unsupported NumPy kind for bigarray");
     }
     int flags = fields->flags;
     enum caml_ba_layout layout;
@@ -171,7 +182,7 @@ bigarray_of_pyarray_wrapper(
         layout = CAML_BA_FORTRAN_LAYOUT;
     }
     else {
-        failwith("Unsupported NumPy layout for bigarray");
+        caml_failwith("Unsupported NumPy layout for bigarray");
     }
     void *data = fields->data;
     bigarray = caml_ba_alloc(kind | layout, nd, data, dims);
diff -pruN 20211015-1/py.ml 20220325-1/py.ml
--- 20211015-1/py.ml	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/py.ml	2022-03-25 15:09:09.000000000 +0000
@@ -7,6 +7,12 @@ type pyobject = Pytypes.pyobject
 
 type input = Pytypes.input = Single | File | Eval
 
+let string_of_input input =
+  match input with
+  | File -> "exec"
+  | Eval -> "eval"
+  | Single -> "single"
+
 type 'a file = 'a Pytypes.file = Filename of string | Channel of 'a
 
 type compare = Pytypes.compare = LT | LE | EQ | NE | GT | GE
@@ -223,7 +229,7 @@ let libpython_from_interpreter python_fu
   let lines = ldd python_full_path in
   let is_libpython line =
     let basename = Filename.basename line in
-    Pyutils.has_prefix "libpython" basename in
+    Stdcompat.String.starts_with ~prefix:"libpython" basename in
   List.find_opt is_libpython lines
 
 let libpython_from_ldconfig major minor =
@@ -236,7 +242,7 @@ let libpython_from_ldconfig major minor
         Printf.sprintf "libpython%d.%d" major' minor' in
   let is_libpython line =
     let basename = Filename.basename line in
-    Pyutils.has_prefix prefix basename in
+    Stdcompat.String.starts_with ~prefix:prefix basename in
   List.find_opt is_libpython lines
 
 let parse_python_list list =
@@ -403,11 +409,16 @@ let libpython_from_pkg_config version_ma
       Some (concat_library_filenames library_paths [library_filename])
   | _ -> None
 
-let library_patterns : (int -> int -> string, unit, string) format list =
+let library_patterns : (int -> int -> string) list =
   match Pyml_arch.os with
-  | Pyml_arch.Windows -> ["python%d%dm.dll"; "python%d%d.dll"]
-  | Pyml_arch.Mac -> ["libpython%d.%dm.dylib"; "libpython%d.%d.dylib"]
-  | Pyml_arch.Linux -> ["libpython%d.%dm.so"; "libpython%d.%d.so"]
+  | Pyml_arch.Windows ->
+      [Printf.sprintf "python%d%dm.dll"; Printf.sprintf "python%d%d.dll"]
+  | Pyml_arch.Mac ->
+      [Printf.sprintf "libpython%d.%dm.dylib";
+        Printf.sprintf "libpython%d.%d.dylib"]
+  | Pyml_arch.Unix ->
+      [Printf.sprintf "libpython%d.%dm.so";
+        Printf.sprintf "libpython%d.%d.so"]
 
 let libpython_from_python_config version_major version_minor =
   let command =
@@ -428,7 +439,7 @@ let libpython_from_python_config version
         List.fold_left parse_word [] word_list in
       let library_filenames =
         List.map
-          (fun format -> Printf.sprintf format version_major version_minor)
+          (fun format -> format version_major version_minor)
           library_patterns in
       Some (concat_library_filenames library_paths library_filenames)
   | _ -> None
@@ -448,7 +459,7 @@ let libpython_from_pythonhome version_ma
         [Filename.concat prefix "lib"] in
   let library_filenames =
     List.map
-      (fun format -> Printf.sprintf format version_major version_minor)
+      (fun format -> format version_major version_minor)
       library_patterns in
   concat_library_filenames library_paths library_filenames
 
@@ -489,14 +500,14 @@ let library_filename = ref None
 
 let load_library filename =
   library_filename := filename;
-  load_library filename
+  load_library filename None
 
 let get_library_filename () = !library_filename
 
 let find_library ~verbose ~version_major ~version_minor ~debug_build
     python_full_path =
   try
-    load_library None debug_build
+    load_library None
   with Failure _ ->
     let library_filenames =
       find_library_path version_major version_minor python_full_path in
@@ -522,7 +533,7 @@ let find_library ~verbose ~version_major
                   Printf.eprintf "Trying to load \"%s\".\n" filename;
                   flush stderr;
                 end;
-              load_library (Some filename) debug_build;
+              load_library (Some filename);
             with Failure msg ->
 (*
               if pythonhome_set then
@@ -637,7 +648,7 @@ let initialize ?library_name ?interprete
   begin
     match library_name with
     | Some library_name ->
-        load_library (Some library_name) debug_build;
+        load_library (Some library_name);
     | None ->
   begin
     try
@@ -718,6 +729,7 @@ let on_finalize f = on_finalize_list :=
 let finalize () =
   assert_initialized ();
   List.iter (fun f -> f ()) !on_finalize_list;
+  Gc.full_major ();
   finalize_library ();
   uninit_pythonhome ();
   uninit_pythonpath ();
@@ -849,8 +861,14 @@ let option result =
   else
     Some result
 
+let assert_not_null function_name obj =
+  if is_null obj then
+    invalid_arg (function_name ^ ": unallowed null argument")
+
 module Eval = struct
   let call_object_with_keywords func arg keyword =
+    assert_not_null "call_object_with_keywords(!, _, _)" func;
+    assert_not_null "call_object_with_keywords(_, !, _)" arg;
     check_not_null (Pywrappers.pyeval_callobjectwithkeywords func arg keyword)
 
   let call_object func arg =
@@ -867,6 +885,7 @@ let object_repr obj = check_not_null (Py
 
 module String_ = struct
   let as_UTF8_string s =
+    assert_not_null "as_UTF8_string" s;
     let f =
       match ucs () with
         UCS2 -> Pywrappers.UCS2.pyunicodeucs2_asutf8string
@@ -901,7 +920,9 @@ module Tuple_ = struct
   let init size f =
     let result = create size in
     for index = 0 to size - 1 do
-      set_item result index (f index)
+      let v = f index in
+      assert_not_null "init" v;
+      set_item result index v
     done;
     result
 
@@ -917,6 +938,8 @@ module Dict_ = struct
     check_not_null (Pywrappers.pydict_new ())
 
   let set_item dict key value =
+    assert_not_null "set_item(!, _)" dict;
+    assert_not_null "set_item(_, !)" key;
     assert_int_success (Pywrappers.pydict_setitem dict key value)
 
   let of_bindings_map fkey fvalue list =
@@ -968,6 +991,8 @@ module Type = struct
   external get: pyobject -> t = "pytype"
 
   let is_subtype a b =
+    assert_not_null "of_tuple5(!, _)" a;
+    assert_not_null "of_tuple5(_, !)" b;
     bool_of_int (Pywrappers.pytype_issubtype a b)
 
   let name t =
@@ -1050,7 +1075,10 @@ module Capsule = struct
         v in
       (wrap, unwrap)
 
-  let type_of x = fst (unsafe_unwrap_value x)
+  let type_of x =
+    if pycapsule_check x = 0 then
+      Type.mismatch "capsule" x;
+    fst (unsafe_unwrap_value x)
 end
 
 module Mapping = struct
@@ -1060,7 +1088,7 @@ module Mapping = struct
     option (Pywrappers.pymapping_getitemstring mapping key)
 
   let find_string mapping key =
-    Pyutils.option_unwrap (get_item_string mapping key)
+    Stdcompat.Option.get (get_item_string mapping key)
 
   let find_string_opt = get_item_string
 
@@ -1079,11 +1107,18 @@ end
 
 module Method = struct
   let create func self cl =
+    assert_not_null "create(!, _, _)" func;
+    assert_not_null "create(_, !, _)" self;
+    assert_not_null "create(_, _, !)" cl;
     check_not_null (Pywrappers.pymethod_new func self cl)
 
-  let get_function m = check_not_null (Pywrappers.pymethod_function m)
-
-  let self m = option (Pywrappers.pymethod_self m)
+  let get_function m =
+    assert_not_null "get_function" m;
+    check_not_null (Pywrappers.pymethod_function m)
+
+  let self m =
+    assert_not_null "self" m;
+    option (Pywrappers.pymethod_self m)
 end
 
 module Bool = struct
@@ -1211,6 +1246,7 @@ module String__ = struct
     check_not_null (f int_array size')
 
   let to_unicode s =
+    assert_not_null "to_unicode" s;
     let f =
       match ucs () with
         UCS2 -> Pywrappers.UCS2.pyunicodeucs2_asunicode
@@ -1411,60 +1447,80 @@ module Object = struct
   type t = Pytypes.pyobject
 
   let del_item obj item =
+    assert_not_null "del_item(!, _)" obj;
+    assert_not_null "del_item(_, !)" item;
     assert_int_success (Pywrappers.pyobject_delitem obj item)
 
   let del_item_string obj item =
     assert_int_success (Pywrappers.pyobject_delitemstring obj item)
 
   let get_attr obj attr =
+    assert_not_null "get_attr(!, _)" obj;
+    assert_not_null "get_attr(_, !)" attr;
     option (Pywrappers.pyobject_getattr obj attr)
 
   let get_attr_string obj attr =
+    assert_not_null "get_attr_string" obj;
     option (Pywrappers.pyobject_getattrstring obj attr)
 
-  let find_attr obj attr = Pyutils.option_unwrap (get_attr obj attr)
+  let find_attr obj attr = Stdcompat.Option.get (get_attr obj attr)
 
   let find_attr_opt = get_attr
 
   let find_attr_string obj attr =
-    Pyutils.option_unwrap (get_attr_string obj attr)
+    Stdcompat.Option.get (get_attr_string obj attr)
 
   let find_attr_string_opt = get_attr_string
 
   let get_item obj key =
     option (Pywrappers.pyobject_getitem obj key)
 
-  let find obj attr = Pyutils.option_unwrap (get_item obj attr)
+  let find obj attr = Stdcompat.Option.get (get_item obj attr)
 
   let find_opt = get_item
 
   let get_item_string obj key = get_item obj (String.of_string key)
 
-  let find_string obj attr = Pyutils.option_unwrap (get_item_string obj attr)
+  let find_string obj attr = Stdcompat.Option.get (get_item_string obj attr)
 
   let find_string_opt = get_item_string
 
   let get_iter obj =
+    assert_not_null "get_iter" obj;
     check_not_null (Pywrappers.pyobject_getiter obj)
 
   let get_type obj =
+    assert_not_null "get_type" obj;
     check_not_null (Pywrappers.pyobject_type obj)
 
   let has_attr obj attr =
+    assert_not_null "has_attr(!, _)" obj;
+    assert_not_null "has_attr(_, !)" attr;
     bool_of_int (Pywrappers.pyobject_hasattr obj attr)
 
   let has_attr_string obj attr =
+    assert_not_null "has_attr_string" obj;
     bool_of_int (Pywrappers.pyobject_hasattrstring obj attr)
 
-  let hash obj = check_int64 (Pywrappers.pyobject_hash obj)
-
-  let is_true obj = bool_of_int (Pywrappers.pyobject_istrue obj)
-
-  let not obj = bool_of_int (Pywrappers.pyobject_istrue obj)
-
-  let is_instance obj cls = bool_of_int (Pywrappers.pyobject_isinstance obj cls)
+  let hash obj =
+    assert_not_null "hash" obj;
+    check_int64 (Pywrappers.pyobject_hash obj)
+
+  let is_true obj =
+    assert_not_null "is_true" obj;
+    bool_of_int (Pywrappers.pyobject_istrue obj)
+
+  let not obj =
+    assert_not_null "not" obj;
+    bool_of_int (Pywrappers.pyobject_istrue obj)
+
+  let is_instance obj cls =
+    assert_not_null "is_instance" obj;
+    bool_of_int (Pywrappers.pyobject_isinstance obj cls)
 
   let is_subclass cls1 cls2 =
+    assert_not_null "is_subclass(!, _)" cls1;
+    assert_not_null "is_subclass(_, !)" cls2;
     bool_of_int (Pywrappers.pyobject_issubclass cls1 cls2)
 
   let print obj out_channel =
@@ -1481,9 +1537,12 @@ module Object = struct
     bool_of_int (Pywrappers.pyobject_richcomparebool a b cmp)
 
   let set_attr obj attr value =
+    assert_not_null "set_attr(!, _, _)" obj;
+    assert_not_null "set_attr(_, !, _)" attr;
     assert_int_success (Pywrappers.pyobject_setattr obj attr value)
 
   let set_attr_string obj attr value =
+    assert_not_null "set_attr_string" obj;
     assert_int_success (Pywrappers.pyobject_setattrstring obj attr value)
 
   let del_attr obj attr = set_attr obj attr null
@@ -1532,15 +1591,20 @@ module Object = struct
     Format.pp_print_string fmt (robust_to_string true v)
 
   let call_method_obj_args obj name args =
+    assert_not_null "call_method_obj_args(!, _, _)" obj;
+    assert_not_null "call_method_obj_args(_, !, _)" name;
     check_not_null (pyobject_callmethodobjargs obj name args)
 
   let call_method obj name args =
     call_method_obj_args obj (String.of_string name) args
 
   let call callable args kw =
+    assert_not_null "call(!, _, _)" callable;
+    assert_not_null "call(_, !, _)" args;
     check_not_null (Pywrappers.pyobject_call callable args kw)
 
   let size obj =
+    assert_not_null "size" obj;
     check_int (Pywrappers.pyobject_size obj)
 end
 
@@ -1612,87 +1676,147 @@ end
 module Number = struct
   let absolute v = check_not_null (Pywrappers.pynumber_absolute v)
 
-  let add v0 v1 = check_not_null (Pywrappers.pynumber_add v0 v1)
-
-  let number_and v0 v1 = check_not_null (Pywrappers.pynumber_and v0 v1)
+  let add v0 v1 =
+    assert_not_null "add(!, _)" v0;
+    assert_not_null "add(_, !)" v1;
+    check_not_null (Pywrappers.pynumber_add v0 v1)
+
+  let number_and v0 v1 =
+    assert_not_null "number_and(!, _)" v0;
+    assert_not_null "number_and(_, !)" v1;
+    check_not_null (Pywrappers.pynumber_and v0 v1)
 
   let _check v = Pywrappers.pynumber_check v <> 0
 
-  let divmod v0 v1 = check_not_null (Pywrappers.pynumber_divmod v0 v1)
+  let divmod v0 v1 =
+    assert_not_null "divmod(!, _)" v0;
+    assert_not_null "divmod(_, !)" v1;
+    check_not_null (Pywrappers.pynumber_divmod v0 v1)
 
   let float v = check_not_null (Pywrappers.pynumber_float v)
 
   let floor_divide v0 v1 =
+    assert_not_null "floor_divide(!, _)" v0;
+    assert_not_null "floor_divide(_, !)" v1;
     check_not_null (Pywrappers.pynumber_floordivide v0 v1)
 
-  let in_place_add v0 v1 = check_not_null (Pywrappers.pynumber_inplaceadd v0 v1)
-
-  let in_place_and v0 v1 = check_not_null (Pywrappers.pynumber_inplaceand v0 v1)
+  let in_place_add v0 v1 =
+    assert_not_null "in_place_add(!, _)" v0;
+    assert_not_null "in_place_add(_, !)" v1;
+    check_not_null (Pywrappers.pynumber_inplaceadd v0 v1)
+
+  let in_place_and v0 v1 =
+    assert_not_null "in_place_and(!, _)" v0;
+    assert_not_null "in_place_and(_, !)" v1;
+    check_not_null (Pywrappers.pynumber_inplaceand v0 v1)
 
   let in_place_floor_divide v0 v1 =
+    assert_not_null "in_place_floor_divide(!, _)" v0;
+    assert_not_null "in_place_floor_divide(_, !)" v1;
     check_not_null (Pywrappers.pynumber_inplacefloordivide v0 v1)
 
   let in_place_lshift v0 v1 =
+    assert_not_null "in_place_lshift(!, _)" v0;
+    assert_not_null "in_place_lshift(_, !)" v1;
     check_not_null (Pywrappers.pynumber_inplacelshift v0 v1)
 
   let in_place_multiply v0 v1 =
+    assert_not_null "in_place_multiply(!, _)" v0;
+    assert_not_null "in_place_multiply(_, !)" v1;
     check_not_null (Pywrappers.pynumber_inplacemultiply v0 v1)
 
   let in_place_or v0 v1 =
+    assert_not_null "in_place_or(!, _)" v0;
+    assert_not_null "in_place_or(_, !)" v1;
     check_not_null (Pywrappers.pynumber_inplaceor v0 v1)
 
   let in_place_power ?(modulo = none) v0 v1 =
+    assert_not_null "in_place_power(?modulo:!, _, _)" modulo;
+    assert_not_null "in_place_power(?modulo:_, !, _)" v0;
+    assert_not_null "in_place_power(?modulo:_, _, _)" v1;
     check_not_null (Pywrappers.pynumber_inplacepower v0 v1 modulo)
 
   let in_place_remainder v0 v1 =
+    assert_not_null "in_place_remainder(!, _)" v0;
+    assert_not_null "in_place_remainder(_, !)" v1;
     check_not_null (Pywrappers.pynumber_inplaceremainder v0 v1)
 
   let in_place_rshift v0 v1 =
+    assert_not_null "in_place_rshift(!, _)" v0;
+    assert_not_null "in_place_rshift(_, !)" v1;
     check_not_null (Pywrappers.pynumber_inplacershift v0 v1)
 
   let in_place_subtract v0 v1 =
+    assert_not_null "in_place_substract(!, _)" v0;
+    assert_not_null "in_place_substract(_, !)" v1;
     check_not_null (Pywrappers.pynumber_inplacesubtract v0 v1)
 
   let in_place_true_divide v0 v1 =
+    assert_not_null "in_place_true_divide(!, _)" v0;
+    assert_not_null "in_place_true_divide(_, !)" v1;
     check_not_null (Pywrappers.pynumber_inplacetruedivide v0 v1)
 
   let in_place_xor v0 v1 =
+    assert_not_null "in_place_xor(!, _)" v0;
+    assert_not_null "in_place_xor(_, !)" v1;
     check_not_null (Pywrappers.pynumber_inplacexor v0 v1)
 
   let invert v =
+    assert_not_null "invert" v;
     check_not_null (Pywrappers.pynumber_invert v)
 
   let lshift v0 v1 =
+    assert_not_null "in_place_xor(!, _)" v0;
+    assert_not_null "in_place_xor(_, !)" v1;
     check_not_null (Pywrappers.pynumber_lshift v0 v1)
 
   let multiply v0 v1 =
+    assert_not_null "in_place_xor(!, _)" v0;
+    assert_not_null "in_place_xor(_, !)" v1;
     check_not_null (Pywrappers.pynumber_multiply v0 v1)
 
   let negative v =
+    assert_not_null "negative" v;
     check_not_null (Pywrappers.pynumber_negative v)
 
   let number_or v0 v1 =
+    assert_not_null "in_place_xor(!, _)" v0;
+    assert_not_null "in_place_xor(_, !)" v1;
     check_not_null (Pywrappers.pynumber_or v0 v1)
 
   let positive v =
+    assert_not_null "positive" v;
     check_not_null (Pywrappers.pynumber_positive v)
 
   let power ?(modulo = none) v0 v1 =
+    assert_not_null "in_place_power(?modulo:!, _, _)" modulo;
+    assert_not_null "in_place_power(?modulo:_, !, _)" v0;
+    assert_not_null "in_place_power(?modulo:_, _, _)" v1;
     check_not_null (Pywrappers.pynumber_power v0 v1 modulo)
 
   let remainder v0 v1 =
+    assert_not_null "remainder(!, _)" v0;
+    assert_not_null "remainder(_, !)" v1;
     check_not_null (Pywrappers.pynumber_remainder v0 v1)
 
   let rshift v0 v1 =
+    assert_not_null "rshift(!, _)" v0;
+    assert_not_null "rshift(_, !)" v1;
     check_not_null (Pywrappers.pynumber_rshift v0 v1)
 
   let subtract v0 v1 =
+    assert_not_null "substract(!, _)" v0;
+    assert_not_null "substract(_, !)" v1;
     check_not_null (Pywrappers.pynumber_subtract v0 v1)
 
   let true_divide v0 v1 =
+    assert_not_null "true_divide_xor(!, _)" v0;
+    assert_not_null "true_divide_xor(_, !)" v1;
     check_not_null (Pywrappers.pynumber_truedivide v0 v1)
 
   let number_xor v0 v1 =
+    assert_not_null "number_xor(!, _)" v0;
+    assert_not_null "number_xor(_, !)" v1;
     check_not_null (Pywrappers.pynumber_xor v0 v1)
 
   let check v =
@@ -1739,7 +1863,9 @@ end
 module Iter_ = struct
   let check o = Type.get o = Type.Iter
 
-  let next i = option (Pywrappers.pyiter_next i)
+  let next i =
+    assert_not_null "next" i;
+    option (Pywrappers.pyiter_next i)
 
   let rec iter f i =
     match next i with
@@ -1798,11 +1924,15 @@ module Sequence = struct
 
   let concat s s' = check_not_null (Pywrappers.pysequence_concat s s')
 
-  let contains s value = bool_of_int (Pywrappers.pysequence_contains s value)
+  let contains s value =
+    assert_not_null "contains(!, _)" s;
+    assert_not_null "contains(_, !)" value;
+    bool_of_int (Pywrappers.pysequence_contains s value)
 
   let count s value = check_int (Pywrappers.pysequence_count s value)
 
   let del_item s index =
+    assert_not_null "del_item" s;
     assert_int_success (Pywrappers.pysequence_delitem s index)
 
   let fast s msg = check_not_null (Pywrappers.pysequence_fast s msg)
@@ -1837,7 +1967,9 @@ module Sequence = struct
   let set_slice s i0 i1 value =
     assert_int_success (Pywrappers.pysequence_setslice s i0 i1 value)
 
-  let size s = check_int (Pywrappers.pysequence_size s)
+  let size s =
+    assert_not_null "size" s;
+    check_int (Pywrappers.pysequence_size s)
 
   let tuple sequence = check_not_null (Pywrappers.pysequence_tuple sequence)
 
@@ -1902,11 +2034,14 @@ module Tuple = struct
 
   let of_seq s = of_array (Array.of_seq s)
 
-  let of_tuple1 v0 = init 1 (function _ -> v0)
+  let of_tuple1 v0 =
+    init 1 (function _ -> v0)
 
-  let of_tuple2 (v0, v1) = init 2 (function 0 -> v0 | _ -> v1)
+  let of_tuple2 (v0, v1) =
+    init 2 (function 0 -> v0 | _ -> v1)
 
-  let of_tuple3 (v0, v1, v2) = init 3 (function 0 -> v0 | 1 -> v1 | _ -> v2)
+  let of_tuple3 (v0, v1, v2) =
+    init 3 (function 0 -> v0 | 1 -> v1 | _ -> v2)
 
   let of_tuple4 (v0, v1, v2, v3) =
     init 4 (function 0 -> v0 | 1 -> v1 | 2 -> v2 | _ -> v3)
@@ -1939,27 +2074,35 @@ module Dict = struct
 
   let check o = Type.get o = Type.Dict
 
-  let clear = Pywrappers.pydict_clear
+  let clear o =
+    assert_not_null "clear" o;
+    Pywrappers.pydict_clear o
 
   let copy v = check_not_null (Pywrappers.pydict_copy v)
 
   let del_item dict item =
+    assert_not_null "del_item(!, _)" dict;
+    assert_not_null "del_item(_, !)" item;
     assert_int_success (Pywrappers.pydict_delitem dict item)
 
   let del_item_string dict name =
+    assert_not_null "del_item_string" dict;
     assert_int_success (Pywrappers.pydict_delitemstring dict name)
 
   let get_item dict key =
+    assert_not_null "get_item(!, _)" dict;
+    assert_not_null "get_item(_, !)" key;
     option (Pywrappers.pydict_getitem dict key)
 
-  let find dict key = Pyutils.option_unwrap (get_item dict key)
+  let find dict key = Stdcompat.Option.get (get_item dict key)
 
   let find_opt = get_item
 
   let get_item_string dict name =
+    assert_not_null "get_item_string" dict;
     option (Pywrappers.pydict_getitemstring dict name)
 
-  let find_string dict key = Pyutils.option_unwrap (get_item_string dict key)
+  let find_string dict key = Stdcompat.Option.get (get_item_string dict key)
 
   let find_string_opt = get_item_string
 
@@ -1968,6 +2111,7 @@ module Dict = struct
   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 =
@@ -2012,35 +2156,50 @@ module Dict = struct
 
   let to_bindings_string = to_bindings_map String.to_string id
 
-  let singleton key value = of_bindings [(key, value)]
-
-  let singleton_string key value = of_bindings_string [(key, value)]
+  let singleton key value =
+    assert_not_null "singleton(!, _)" key;
+    assert_not_null "singleton(_, !)" value;
+    of_bindings [(key, value)]
+
+  let singleton_string key value =
+    assert_not_null "singleton_string" value;
+    of_bindings_string [(key, value)]
 end
 
 module Set = struct
   let check o = Type.get o = Type.Set
 
-  let clear = Pywrappers.pyset_clear
+  let clear o =
+    assert_not_null "clear" o;
+    Pywrappers.pyset_clear o
 
   let copy v = check_not_null (Pywrappers.pyset_new v)
 
   let create () = check_not_null (Pywrappers.pyset_new null)
 
   let size set =
+    assert_not_null "size" set;
     let sz = Pywrappers.pyset_size set in
     assert_int_success sz;
     sz
 
   let add set value =
+    assert_not_null "add(!, _)" set;
+    assert_not_null "add(_, !)" value;
     assert_int_success (Pywrappers.pyset_add set value)
 
   let contains set value =
+    assert_not_null "contains(!, _)" set;
+    assert_not_null "contains(_, !)" value;
     bool_of_int (Pywrappers.pyset_contains set value)
 
   let discard set value =
+    assert_not_null "discard(!, _)" set;
+    assert_not_null "discard(_, !)" value;
     assert_int_success (Pywrappers.pyset_discard set value)
 
   let to_list_map f set =
+    assert_not_null "to_list_map" set;
     Iter_.to_list_map f (Object.get_iter set)
 
   let to_list = to_list_map id
@@ -2075,7 +2234,7 @@ module Traceback = struct
         let args =
           Tuple.of_array [| acc; create_frame frame; Int.of_int 0; Int.of_int frame.line_number |]
         in
-        Eval.call_object tb_type args)
+        Object.call tb_type args null)
       none
       t
 end
@@ -2125,13 +2284,13 @@ module Callable = struct
     if not (check c) then
       Type.mismatch "Callable" c;
     function args ->
-      Eval.call_object c args
+      Object.call c args null
 
   let to_function_as_tuple_and_dict c =
     if not (check c) then
       Type.mismatch "Callable" c;
     fun args keywords ->
-      Eval.call_object_with_keywords c args keywords
+      Object.call c args keywords
 
   let to_function c =
     let f = to_function_as_tuple c in
@@ -2143,6 +2302,15 @@ module Callable = struct
       f (Tuple.of_array args) (Dict.of_bindings_string keywords)
 end
 
+type optimize = Default | Debug | Normal | RemoveDocstrings
+
+let int_of_optimize opt =
+  match opt with
+  | Default -> -1
+  | Debug -> 0
+  | Normal -> 1
+  | RemoveDocstrings -> 2
+
 module Import = struct
 (* This function has been removed from Python 3.9, and was marked
   "for internal use only" before.
@@ -2151,12 +2319,43 @@ module Import = struct
 
   let add_module name = check_not_null (Pywrappers.pyimport_addmodule name)
 
+  let main () = add_module "__main__"
+
+  let builtins () = Object.find_attr_string (main ()) "__builtins__"
+
+  let compile ~source ~filename ?(dont_inherit = false)
+      ?(optimize = Default) mode =
+    let compile =
+      Callable.to_function_with_keywords
+        (Object.find_attr_string (builtins ()) "compile") in
+    let source = String.of_string source in
+    let filename = String.of_string filename in
+    let mode = String.of_string (string_of_input mode) in
+    let dont_inherit = Bool.of_bool dont_inherit in
+    let args = ["dont_inherit", dont_inherit] in
+    let args =
+      if !version_minor_value <= 2 then
+        args
+      else
+        begin
+          let optimize = Int.of_int (int_of_optimize optimize) in
+          ["optimize", optimize]
+        end in
+    compile [| source; filename; mode |] args
+
   let exec_code_module name obj =
+    assert_not_null "exec_code_module" obj;
     check_not_null (Pywrappers.pyimport_execcodemodule name obj)
 
   let exec_code_module_ex name obj pathname =
+    assert_not_null "exec_code_module_ex" obj;
     check_not_null (Pywrappers.pyimport_execcodemoduleex name obj pathname)
 
+  let exec_code_module_from_string ~name ?(filename = name)
+        ?dont_inherit ?optimize source =
+    let obj = compile ~source ~filename ?dont_inherit ?optimize File in
+    exec_code_module name obj
+
   let get_magic_number = Pywrappers.pyimport_getmagicnumber
 
   let get_module_dict () =
@@ -2203,12 +2402,15 @@ module Module = struct
     check_not_null (Pywrappers.pymodule_new name)
 
   let get_dict m =
+    assert_not_null "get_dict" m;
     check_not_null (Pywrappers.pymodule_getdict m)
 
   let get_filename m =
+    assert_not_null "get_filename" m;
     check_some (Pywrappers.pymodule_getfilename m)
 
   let get_name m =
+    assert_not_null "get_name" m;
     check_some (Pywrappers.pymodule_getname m)
 
   let get = Object.find_attr_string
@@ -2234,12 +2436,14 @@ module Module = struct
 
   let remove = Object.del_attr_string
 
-  let main () = Import.add_module "__main__"
+  let main = Import.main
 
   let sys () = Import.import_module "sys"
 
   let builtins () = get (main ()) "__builtins__"
 
+  let compile = Import.compile
+
   let set_docstring m doc =
     Pywrappers.pymodule_setdocstring m doc
     |> assert_int_success
@@ -2278,7 +2482,8 @@ module Iter = struct
     let iter_fn = Callable.of_function (function
       | [||] -> failwith "__iter__ expects at least one argument"
       | array -> array.(0)) in
-    let methods = [next_name, Callable.of_function next'; "__iter__", iter_fn] in
+    let methods =
+      [next_name, Callable.of_function next'; "__iter__", iter_fn] in
     Object.call_function_obj_args
       (Class.init ~methods "iterator") [| |]
 
@@ -2358,6 +2563,8 @@ module Iter = struct
     check_not_null (Pywrappers.pyseqiter_new seq)
 
   let call_iter call sentinel =
+    assert_not_null "call_iter(!, _)" call;
+    assert_not_null "call_iter(_, !)" sentinel;
     check_not_null (Pywrappers.pycalliter_new call sentinel)
 
   (* As a sentinel we use a function so that there is no collision risk.
@@ -2383,7 +2590,9 @@ module List = struct
 
   let create size = check_not_null (Pywrappers.pylist_new size)
 
-  let size list = check_int (Pywrappers.pylist_size list)
+  let size list =
+    assert_not_null "size" list;
+    check_int (Pywrappers.pylist_size list)
 
   let length = size
 
@@ -2410,7 +2619,9 @@ module List = struct
 
   let of_sequence = Sequence.list
 
-  let singleton v = init 1 (fun _ -> v)
+  let singleton v =
+    assert_not_null "singleton" v;
+    init 1 (fun _ -> v)
 
   let of_seq s = of_array (Array.of_seq s)
 end
@@ -2658,23 +2869,17 @@ let set_argv argv =
 
 let last_value () = Module.get (Module.builtins ()) "_"
 
-let compile ~source ~filename ?(dont_inherit = false)
-    ?(optimize = `Default) mode =
-  let compile =
-    Module.get_function_with_keywords (Module.builtins ()) "compile" in
-  let source = String.of_string source in
-  let filename = String.of_string filename in
+let compile ~source ~filename ?dont_inherit ?optimize mode =
   let mode =
-    String.of_string @@ match mode with
-    | `Exec -> "exec"
-    | `Eval -> "eval"
-    | `Single -> "single" in
+    match mode with
+    | `Exec -> File
+    | `Eval -> Eval
+    | `Single -> Single in
   let optimize =
-    Int.of_int @@ match optimize with
-    | `Default -> -1
-    | `Debug -> 0
-    | `Normal -> 1
-    | `RemoveDocstrings -> 2 in
-  let dont_inherit = Bool.of_bool dont_inherit in
-  compile [| source; filename; mode |]
-    ["dont_inherit", dont_inherit; "optimize", optimize]
+    Stdcompat.Option.map (function
+        | `Default -> Default
+        | `Debug -> Debug
+        | `Normal -> Normal
+        | `RemoveDocstrings -> RemoveDocstrings)
+      optimize in
+  Module.compile ~source ~filename ?dont_inherit ?optimize mode
diff -pruN 20211015-1/pyml_arch_generate.c 20220325-1/pyml_arch_generate.c
--- 20211015-1/pyml_arch_generate.c	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyml_arch_generate.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,27 +0,0 @@
-#include <stdio.h>
-
-#if __APPLE__
-  #define PLATFORM_NAME "Mac"
-#elif __linux__
-  #define PLATFORM_NAME "Linux"
-#elif defined(WIN32) || defined(_WIN32)
-  #define PLATFORM_NAME "Windows"
-#else
-  #error "Unknown platform"
-#endif
-
-int
-main(int argc, char* argv[])
-{
-  FILE *f = fopen("pyml_arch.ml", "w");
-  fprintf(f, "\
-type t = Linux | Windows | Mac\n\n\
-let os = %s\n\n\
-", PLATFORM_NAME);
-  #if WIN32
-    fprintf(f, "%s", "external fd_of_int : int -> Unix.file_descr = \"win_handle_fd\"");
-  #else
-    fprintf(f, "%s", "external fd_of_int : int -> Unix.file_descr = \"%identity\"");
-  #endif
-  fclose(f);
-} 
diff -pruN 20211015-1/pyml_arch.ml.c 20220325-1/pyml_arch.ml.c
--- 20211015-1/pyml_arch.ml.c	1970-01-01 00:00:00.000000000 +0000
+++ 20220325-1/pyml_arch.ml.c	2022-03-25 15:09:09.000000000 +0000
@@ -0,0 +1,20 @@
+#if __APPLE__
+  #define PLATFORM_NAME Mac
+#elif defined(WIN32) || defined(_WIN32)
+  #define PLATFORM_NAME Windows
+  #define WIN_HANDLE_FD
+#elif unix
+  #define PLATFORM_NAME Unix
+#else
+  #error "Unknown platform"
+#endif
+
+type t = Windows | Mac | Unix
+
+let os = PLATFORM_NAME
+
+#ifdef WIN_HANDLE_FD
+  external fd_of_int : int -> Unix.file_descr = "win_handle_fd"
+#else
+  external fd_of_int : int -> Unix.file_descr = "%identity"
+#endif
diff -pruN 20211015-1/pyml_arch.mli 20220325-1/pyml_arch.mli
--- 20211015-1/pyml_arch.mli	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyml_arch.mli	2022-03-25 15:09:09.000000000 +0000
@@ -1,4 +1,4 @@
-type t = Linux | Windows | Mac
+type t = Windows | Mac | Unix
 
 val os : t
 
diff -pruN 20211015-1/py.mli 20220325-1/py.mli
--- 20211015-1/py.mli	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/py.mli	2022-03-25 15:09:09.000000000 +0000
@@ -414,6 +414,13 @@ module Callable: sig
       Wrapper for
       {{: https://docs.python.org/3/c-api/object.html#c.PyCallable_Check} PyCallable_Check}. *)
 
+  val handle_errors : ('a -> Object.t) -> 'a -> Object.t
+  (** [handle_errors f x] calls [f x] and returns its result if the call
+      succeeds. If [f x] raises a Python exception
+      ([Py.E (errtype, errvalue)] or [Py.Err (errtype, msg)]),
+      this exception is raised as a Python exception
+      (via {!Err.set_object} or {!Err.set_error} respectively). *)
+
   val of_function_as_tuple: ?name:string -> ?docstring:string -> (Object.t -> Object.t) ->
     Object.t
   (** [of_function_as_tuple f] returns a Python callable object that calls the
@@ -916,6 +923,9 @@ module Float: sig
       {{:https://docs.python.org/3/c-api/float.html#c.PyFloat_FromDouble} PyFloat_FromDouble}. *)
 end
 
+type optimize = Default | Debug | Normal | RemoveDocstrings
+
+val int_of_optimize : optimize -> int
 
 (** Importing Modules *)
 module Import: sig
@@ -931,8 +941,9 @@ module Import: sig
       {{:https://docs.python.org/3/c-api/import.html#c.PyImport_AddModule} PyImport_AddModule} *)
 
   val exec_code_module: string -> Object.t -> Object.t
-  (** [exec_code_module name bytecode] imports the module [name] compiled in [bytecode].
-      [bytecode] can be obtained with {!val:Py.compile}.
+  (** [exec_code_module name bytecode] imports the module [name] compiled in
+      [bytecode]. [bytecode] can be obtained with {!val:Py.Module.compile}
+      (you may also consider {!val:Py.Import.exec_code_module_from_string}.
       Wrapper for
       {{:https://docs.python.org/3/c-api/import.html#c.PyImport_ExecCodeModule} PyImport_ExecCodeModule} *)
 
@@ -940,6 +951,14 @@ module Import: sig
   (** Wrapper for
       {{:https://docs.python.org/3/c-api/import.html#c.PyImport_ExecCodeModuleEx} PyImport_ExecCodeModuleEx} *)
 
+  val exec_code_module_from_string : name:string -> ?filename:string ->
+      ?dont_inherit:bool -> ?optimize:optimize -> string -> Object.t
+  (** [exec_code_module ~name ?filename ?dont_inherit ?optimize source_code]
+      compiles [source_code] and imports the resulting bytecode as
+      module [name]. [filename] is equal to [name] by default and is used
+      in error messages. [dont_inherit] and [optimize] are passed to
+      {!val:Py.Module.compile} for compiling [source_code]. *)
+
   val get_magic_number: unit -> int64
   (** Wrapper for
       {{:https://docs.python.org/3/c-api/import.html#c.PyImport_GetMagicNumber} PyImport_GetMagicNumber} *)
@@ -1242,6 +1261,10 @@ module Method: sig
       {{:https://docs.python.org/3/c-api/method.html#c.PyMethod_Self} PyMethod_Self} *)
 end
 
+type input = Pytypes.input = Single | File | Eval
+
+val string_of_input : input -> string
+
 (** Interface for Python values of type [Module]. *)
 module Module: sig
   val check: Object.t -> bool
@@ -1320,6 +1343,14 @@ module Module: sig
   val set_docstring: Object.t -> string -> unit
   (** Wrapper for
       {{:https://docs.python.org/3/c-api/module.html#c.PyModule_SetDocString} PyModule_SetDocString} *)
+
+  val compile : source:string -> filename:string -> ?dont_inherit:bool ->
+      ?optimize:optimize -> input -> Object.t
+  (** [compile ~source ~filename ?dont_inherit ?optimize mode] returns
+    the bytecode obtained by compiling ~source. It is a wrapper for
+    the built-in function
+    {{:https://docs.python.org/3/library/functions.html#compile} compile()}.
+ {{:https://github.com/thierry-martinez/pyml/issues/25} GitHub issue #25}*)
 end
 
 (** Interface for Python values of type [Number]. *)
@@ -1496,8 +1527,6 @@ module Number: sig
   (** Synomym of {!rshift} *)
 end
 
-type input = Pytypes.input = Single | File | Eval
-
 (** Interface for Python values of type [Run]. *)
 module Run: sig
   val eval: ?start:input -> ?globals:Object.t -> ?locals:Object.t -> string
@@ -1817,7 +1846,7 @@ module Tuple: sig
   (** The empty tuple [()].
       This value is guaranteed to be the unique value associated to [()]. *)
 
-    val is_empty: Object.t -> bool
+  val is_empty: Object.t -> bool
   (** [Py.is_empty v] is true if and only if [v] is [()].
       Since [Py.Tuple.empty] is guaranteed to be the unique value associated to
       [()], [Py.is_empty v] is equivalent to [v == Py.empty]. *)
@@ -2144,8 +2173,4 @@ val exception_printer: exn -> string opt
 val compile: source:string -> filename:string -> ?dont_inherit:bool ->
   ?optimize:[`Default | `Debug | `Normal | `RemoveDocstrings ] ->
   [`Exec | `Eval | `Single] -> Object.t
-(** [compile ~source ~filename ?dont_inherit ?optimize mode] returns
-    the bytecode obtained by compiling ~source. It is a wrapper for
-    the built-in function
-    {{:https://docs.python.org/3/library/functions.html#compile} compile()}.
- {{:https://github.com/thierry-martinez/pyml/issues/25} GitHub issue #25}*)
+(** Old interface for {!val:Py.Module.compile}. *)
diff -pruN 20211015-1/pyml.opam 20220325-1/pyml.opam
--- 20211015-1/pyml.opam	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyml.opam	2022-03-25 15:09:09.000000000 +0000
@@ -9,10 +9,10 @@ homepage: "http://github.com/thierry-mar
 doc: "http://github.com/thierry-martinez/pyml"
 bug-reports: "http://github.com/thierry-martinez/pyml/issues"
 depends: [
-  "dune" {>= "2.9"}
+  "dune" {>= "2.8"}
   "ocaml" {>= "3.12.1"}
   "ocamlfind" {build}
-  "stdcompat" {>= "17"}
+  "stdcompat" {>= "18"}
   "conf-python-3-dev" {with-test}
   "odoc" {with-doc}
 ]
@@ -26,11 +26,10 @@ build: [
     name
     "-j"
     jobs
-    "--promote-install-files=false"
     "@install"
     "@runtest" {with-test}
     "@doc" {with-doc}
   ]
-  ["dune" "install" "-p" name "--create-install-files" name]
 ]
 dev-repo: "git+https://github.com/thierry-martinez/pyml.git"
+version: "20220325"
diff -pruN 20211015-1/pyml_stubs.c 20220325-1/pyml_stubs.c
--- 20211015-1/pyml_stubs.c	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyml_stubs.c	2022-03-25 15:09:09.000000000 +0000
@@ -5,23 +5,25 @@
 #include <caml/callback.h>
 #include <caml/custom.h>
 #include <caml/alloc.h>
+#include <caml/intext.h>
 #include <string.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdcompat.h>
+#include <assert.h>
 #include "pyml_stubs.h"
 
 static FILE *(*Python__Py_fopen)(const char *pathname, const char *mode);
 
-static FILE *(*Python__Py_wfopen)(const wchar_t *pathname, const char *mode);
+static FILE *(*Python__Py_wfopen)(const wchar_t *pathname, const wchar_t *mode);
 
 static void *xmalloc(size_t size)
 {
     void *p = malloc(size);
     if (!p) {
-        failwith("Virtual memory exhausted\n");
+        caml_failwith("Virtual memory exhausted\n");
     }
     return p;
 }
@@ -134,42 +136,6 @@ file_of_file_descr(value file_descr, con
 }
 #endif
 
-/* The following definitions are extracted and simplified from
-#include <Python.h>
-*/
-
-typedef struct {
-    int cf_flags;
-} PyCompilerFlags;
-
-#define Py_TPFLAGS_INT_SUBCLASS         (1L<<23)
-#define Py_TPFLAGS_LONG_SUBCLASS        (1UL << 24)
-#define Py_TPFLAGS_LIST_SUBCLASS        (1UL << 25)
-#define Py_TPFLAGS_TUPLE_SUBCLASS       (1UL << 26)
-#define Py_TPFLAGS_BYTES_SUBCLASS       (1UL << 27)
-#define Py_TPFLAGS_UNICODE_SUBCLASS     (1UL << 28)
-#define Py_TPFLAGS_DICT_SUBCLASS        (1UL << 29)
-#define Py_TPFLAGS_BASE_EXC_SUBCLASS    (1UL << 30)
-#define Py_TPFLAGS_TYPE_SUBCLASS        (1UL << 31)
-
-#define Py_LT 0
-#define Py_LE 1
-#define Py_EQ 2
-#define Py_NE 3
-#define Py_GT 4
-#define Py_GE 5
-
-typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
-
-typedef struct PyMethodDef {
-    const char *ml_name;
-    PyCFunction ml_meth;
-    int ml_flags;
-    const char	*ml_doc;
-} PyMethodDef;
-
-typedef void (*PyCapsule_Destructor)(PyObject *);
-
 static void *Python27__PyObject_NextNotImplemented;
 
 /* Global variables for the library */
@@ -235,6 +201,7 @@ static enum UCS { UCS_NONE, UCS2, UCS4 }
 static PyObject *tuple_empty;
 
 #include "pyml.h"
+#include <stdio.h>
 
 static void *getcustom( value v )
 {
@@ -302,10 +269,102 @@ pyhash( value v )
     }
 }
 
+void
+pyml_assert_initialized()
+{
+    if (!version_major) {
+        caml_failwith("Run 'Py.initialize ()' first");
+    }
+}
+
+/** Creates a Python tuple initialized with a single element given by the
+    argument. The reference to the argument is stolen. */
+static PyObject *
+singleton(PyObject *value)
+{
+    PyObject *result = Python_PyTuple_New(1);
+    if (!result) {
+      caml_failwith("PyTuple_New");
+    }
+    if (Python_PyTuple_SetItem(result, 0, value)) {
+      caml_failwith("PyTuple_SetItem");
+    }
+    return result;
+}
+
+static void
+pyserialize(value v, uintnat *bsize_32, uintnat *bsize_64)
+{
+    pyml_assert_initialized();
+    PyObject *value = getcustom(v);
+    PyObject *pickle = Python_PyImport_ImportModule("pickle");
+    if (pickle == NULL) {
+      caml_failwith("Cannot import pickle");
+    }
+    PyObject *dumps = Python_PyObject_GetAttrString(pickle, "dumps");
+    if (dumps == NULL) {
+      caml_failwith("pickle.dumps unavailable");
+    }
+    PyObject *args = singleton(value);
+    PyObject *bytes = Python_PyObject_Call(dumps, args, NULL);
+    if (bytes == NULL) {
+      caml_failwith("pickle.dumps failed");
+    }
+    Py_ssize_t size;
+    char *contents;
+    if (version_major >= 3) {
+      size = Python3_PyBytes_Size(bytes);
+      contents = (char *) Python3_PyBytes_AsString(bytes);
+    }
+    else {
+      size = Python2_PyString_Size(bytes);
+      contents = (char *) Python2_PyString_AsString(bytes);
+    }
+    caml_serialize_int_8(size);
+    caml_serialize_block_1(contents, size);
+    *bsize_32 = 4;
+    *bsize_64 = 8;
+    /*Py_DECREF(bytes);*/ /* reference stolen by args */
+    Py_DECREF(args);
+    Py_DECREF(dumps);
+    Py_DECREF(pickle);
+}
+
 static uintnat
 pydeserialize(void *dst)
 {
-    return 0L;
+    pyml_assert_initialized();
+    Py_ssize_t size = caml_deserialize_uint_8();
+    PyObject *bytes;
+    char *contents;
+    if (version_major >= 3) {
+      bytes = Python3_PyBytes_FromStringAndSize(NULL, size);
+      contents = (char *) Python3_PyBytes_AsString(bytes);
+    }
+    else {
+      bytes = Python2_PyString_FromStringAndSize(NULL, size);
+      contents = (char *) Python2_PyString_AsString(bytes);
+    }
+    caml_deserialize_block_1(contents, size);
+    PyObject *pickle = Python_PyImport_ImportModule("pickle");
+    if (pickle == NULL) {
+      caml_failwith("Cannot import pickle");
+    }
+    PyObject *loads = Python_PyObject_GetAttrString(pickle, "loads");
+    if (loads == NULL) {
+      caml_failwith("pickle.loads unavailable");
+    }
+    PyObject *args = singleton(bytes);
+    PyObject *value = Python_PyObject_Call(loads, args, NULL);
+    if (value == NULL) {
+      caml_failwith("pickle.loads failed");
+    }
+    *((PyObject **) dst) = value;
+    /*Py_DECREF(bytes);*/ /* reference stolen by args */
+    Py_DECREF(args);
+    Py_DECREF(loads);
+    Py_DECREF(pickle);
+    return sizeof(PyObject *);
 }
 
 struct custom_operations pyops =
@@ -314,7 +373,7 @@ struct custom_operations pyops =
     pydecref,
     pycompare,
     pyhash,
-    custom_serialize_default,
+    pyserialize,
     pydeserialize
 };
 
@@ -335,7 +394,7 @@ resolve(const char *symbol)
         ssize_t size = snprintf(NULL, 0, fmt, symbol);
         char *msg = xmalloc(size + 1);
         snprintf(msg, size + 1, fmt, symbol);
-        failwith(msg);
+        caml_failwith(msg);
     }
     return result;
 }
@@ -544,19 +603,11 @@ caml_aux(PyObject *obj)
 }
 
 void
-pyml_assert_initialized()
-{
-    if (!version_major) {
-        failwith("Run 'Py.initialize ()' first");
-    }
-}
-
-void
 pyml_assert_python2()
 {
     if (version_major != 2) {
         pyml_assert_initialized();
-        failwith("Python 2 needed");
+        caml_failwith("Python 2 needed");
     }
 }
 
@@ -565,7 +616,7 @@ pyml_assert_ucs2()
 {
     if (ucs != UCS2) {
         pyml_assert_initialized();
-        failwith("Python with UCS2 needed");
+        caml_failwith("Python with UCS2 needed");
     }
 }
 
@@ -574,7 +625,7 @@ pyml_assert_ucs4()
 {
     if (ucs != UCS4) {
         pyml_assert_initialized();
-        failwith("Python with UCS4 needed");
+        caml_failwith("Python with UCS4 needed");
     }
 }
 
@@ -583,7 +634,7 @@ pyml_assert_python3()
 {
     if (version_major != 3) {
         pyml_assert_initialized();
-        failwith("Python 3 needed");
+        caml_failwith("Python 3 needed");
     }
 }
 
@@ -594,16 +645,16 @@ pyml_check_symbol_available(void *symbol
         char *fmt = "Symbol unavailable with this version of Python: %s.\n";
         ssize_t size = snprintf(NULL, 0, fmt, symbol_name);
         if (size < 0) {
-          failwith("Symbol unavailable with this version of Python.\n");
+          caml_failwith("Symbol unavailable with this version of Python.\n");
           return;
         }
         char *msg = xmalloc(size + 1);
         size = snprintf(msg, size + 1, fmt, symbol_name);
         if (size < 0) {
-          failwith("Symbol unavailable with this version of Python.\n");
+          caml_failwith("Symbol unavailable with this version of Python.\n");
           return;
         }
-        failwith(msg);
+        caml_failwith(msg);
     }
 }
 
@@ -671,6 +722,51 @@ pyml_wrap_closure(value name, value docs
 
 int debug_build;
 
+int trace_refs_build;
+
+static void
+guess_debug_build()
+{
+    PyObject *sysconfig = Python_PyImport_ImportModule("sysconfig");
+    if (!sysconfig) {
+        caml_failwith("Cannot import sysconfig");
+    }
+    PyObject *get_config_var =
+        Python_PyObject_GetAttrString(sysconfig, "get_config_var");
+    assert(get_config_var);
+    PyObject *args;
+    PyObject *py_debug;
+    PyObject *debug_build_py;
+    char *py_debug_str = "Py_DEBUG";
+    if (version_major >= 3) {
+        py_debug = Python3_PyUnicode_FromStringAndSize(py_debug_str, 8);
+    }
+    else {
+        py_debug = Python2_PyString_FromStringAndSize(py_debug_str, 8);
+    }
+    assert(py_debug);
+    args = singleton(py_debug);
+    debug_build_py = Python_PyObject_Call(get_config_var, args, NULL);
+    assert(debug_build_py);
+    if (debug_build_py == Python__Py_NoneStruct) {
+        debug_build = 0;
+    }
+    else {
+        if (version_major >= 3) {
+            debug_build = Python_PyLong_AsLong(debug_build_py);
+        }
+        else {
+            debug_build = Python2_PyInt_AsLong(debug_build_py);
+        }
+        if (debug_build ==  -1) {
+            caml_failwith("Cannot check for debug build");
+        }
+    }
+    Py_DECREF(args);
+    Py_DECREF(get_config_var);
+    Py_DECREF(sysconfig);
+}
+
 CAMLprim value
 py_load_library(value filename_ocaml, value debug_build_ocaml)
 {
@@ -679,7 +775,7 @@ py_load_library(value filename_ocaml, va
         const char *filename = String_val(Field(filename_ocaml, 0));
         library = open_library(filename);
         if (!library) {
-            failwith(get_library_error());
+            caml_failwith(get_library_error());
         }
     }
     else {
@@ -687,7 +783,7 @@ py_load_library(value filename_ocaml, va
     }
     Python_Py_GetVersion = find_symbol(library, "Py_GetVersion");
     if (!Python_Py_GetVersion) {
-        failwith("No Python symbol");
+        caml_failwith("No Python symbol");
     }
     const char *version = Python_Py_GetVersion();
     version_major = version[0] - '0';
@@ -742,54 +838,19 @@ py_load_library(value filename_ocaml, va
     }
 #include "pyml_dlsyms.inc"
     Python_Py_Initialize();
+    PyObject *sys = Python_PyImport_ImportModule("sys");
+    if (!sys) {
+      caml_failwith("cannot import module sys");
+    }
+    trace_refs_build = Python_PyObject_HasAttrString(sys, "getobjects");
     if (Is_block(debug_build_ocaml)) {
         debug_build = Int_val(Field(debug_build_ocaml, 0));
     }
     else {
-        PyObject *sysconfig = Python_PyImport_ImportModule("sysconfig");
-        PyObject *get_config_var =
-            Python_PyObject_GetAttrString(sysconfig, "get_config_var");
-        PyObject *args;
-        PyObject *py_debug;
-        PyObject *debug_build_py;
-        char *py_debug_str = "Py_DEBUG";
-        if (version_major >= 3) {
-            py_debug = Python3_PyUnicode_FromStringAndSize(py_debug_str, 8);
-        }
-        else {
-            py_debug = Python2_PyString_FromStringAndSize(py_debug_str, 8);
-        }
-        if (!py_debug) {
-            failwith("py_debug");
-        }
-        args = Python_PyTuple_New(1);
-        if (!args) {
-            failwith("PyTuple_New");
-        }
-        if (Python_PyTuple_SetItem(args, 0, py_debug)) {
-            failwith("PyTuple_SetItem");
-        }
-        debug_build_py =
-            Python_PyEval_CallObjectWithKeywords(get_config_var, args, NULL);
-        if (!debug_build_py) {
-            failwith("PyEval_CallObjectWithKeywords");
-        }
-        if (debug_build_py == Python__Py_NoneStruct) {
-            debug_build = 0;
-        }
-        else {
-            if (version_major >= 3) {
-                debug_build = Python_PyLong_AsLong(debug_build_py);
-            }
-            else {
-                debug_build = Python2_PyInt_AsLong(debug_build_py);
-            }
-            if (debug_build ==  -1) {
-                failwith("Cannot check for debug build");
-            }
-        }
+        guess_debug_build();
     }
     tuple_empty = Python_PyTuple_New(0);
+    caml_register_custom_operations(&pyops);
     CAMLreturn(Val_unit);
 }
 
@@ -800,7 +861,7 @@ struct PyObjectDebug {
 };
 
 PyObjectDescr *pyobjectdescr(PyObject *obj) {
-    if (debug_build) {
+    if (trace_refs_build) {
         return &((struct PyObjectDebug *) obj)->descr;
     }
     else {
@@ -835,7 +896,7 @@ py_unsetenv(value name_ocaml)
     CAMLparam1(name_ocaml);
     const char *name = String_val(name_ocaml);
     if (unsetenv(name) == -1) {
-        failwith(strerror(errno));
+        caml_failwith(strerror(errno));
     }
     CAMLreturn(Val_unit);
 }
@@ -1112,7 +1173,7 @@ pyml_capsule_check(value v)
 {
     CAMLparam1(v);
     pyml_assert_initialized();
-    PyObject *o = getcustom(v);
+    PyObject *o = pyml_unwrap(v);
     PyObject *ob_type = pyobjectdescr(o)->ob_type;
     int check_result = ob_type == Python_PyCapsule_Type;
     CAMLreturn(Val_int(check_result));
@@ -1226,7 +1287,7 @@ wide_string_of_string(const char *s)
         exit(EXIT_FAILURE);
     }
     wchar_t *ws = xmalloc((n + 1) * sizeof (wchar_t));
-    mbstowcs(ws, s, n);
+    mbstowcs(ws, s, n + 1);
     return ws;
 }
 
@@ -1350,7 +1411,9 @@ open_file(value file, const char *mode)
         }
         else if (Python__Py_wfopen != NULL) {
             wchar_t *wide_filename = wide_string_of_string(filename);
-            result = Python__Py_wfopen(wide_filename, mode);
+            wchar_t *wide_mode = wide_string_of_string(mode);
+            result = Python__Py_wfopen(wide_filename, wide_mode);
+            free(wide_mode);
             free(wide_filename);
         }
         else {
@@ -1454,7 +1517,7 @@ Python27_PyCapsule_IsValid_wrapper(value
 
     pyml_assert_initialized();
     if (!Python27_PyCapsule_IsValid) {
-        failwith("PyCapsule_IsValid is only available in Python >2.7");
+        caml_failwith("PyCapsule_IsValid is only available in Python >2.7");
     }
     PyObject *arg0 = pyml_unwrap(arg0_ocaml);
     const char *arg1 = String_val(arg1_ocaml);
diff -pruN 20211015-1/pyml_stubs.h 20220325-1/pyml_stubs.h
--- 20211015-1/pyml_stubs.h	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyml_stubs.h	2022-03-25 15:09:09.000000000 +0000
@@ -2,6 +2,8 @@
 #define _PYML_STUBS_H_
 #include <stdbool.h>
 #include <stdint.h>
+#include <stdio.h>
+#include <stddef.h>
 
 /* The following definitions are extracted and simplified from
 #include <Python.h>
@@ -113,12 +115,6 @@ pyml_assert_ucs4();
 void
 pyml_assert_python3();
 
-value
-pyml_wrap(PyObject *object, bool steal);
-
-PyObject *
-pyml_unwrap(value v);
-
 /* Numpy */
 
 /* from ndarraytypes.h */
@@ -231,9 +227,36 @@ typedef struct tagPyArrayObject_fields {
     PyObject *weakreflist;
 } PyArrayObject_fields;
 
-struct numpy_custom_operations {
-    struct custom_operations ops;
-    PyObject *obj;
-};
+typedef struct {
+    int cf_flags;
+} PyCompilerFlags;
+
+#define Py_TPFLAGS_INT_SUBCLASS         (1L<<23)
+#define Py_TPFLAGS_LONG_SUBCLASS        (1UL << 24)
+#define Py_TPFLAGS_LIST_SUBCLASS        (1UL << 25)
+#define Py_TPFLAGS_TUPLE_SUBCLASS       (1UL << 26)
+#define Py_TPFLAGS_BYTES_SUBCLASS       (1UL << 27)
+#define Py_TPFLAGS_UNICODE_SUBCLASS     (1UL << 28)
+#define Py_TPFLAGS_DICT_SUBCLASS        (1UL << 29)
+#define Py_TPFLAGS_BASE_EXC_SUBCLASS    (1UL << 30)
+#define Py_TPFLAGS_TYPE_SUBCLASS        (1UL << 31)
+
+#define Py_LT 0
+#define Py_LE 1
+#define Py_EQ 2
+#define Py_NE 3
+#define Py_GT 4
+#define Py_GE 5
+
+typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
+
+typedef struct PyMethodDef {
+    const char *ml_name;
+    PyCFunction ml_meth;
+    int ml_flags;
+    const char	*ml_doc;
+} PyMethodDef;
+
+typedef void (*PyCapsule_Destructor)(PyObject *);
 
 #endif /* _PYML_STUBS_H_ */
diff -pruN 20211015-1/pyml_tests_common.ml 20220325-1/pyml_tests_common.ml
--- 20211015-1/pyml_tests_common.ml	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyml_tests_common.ml	2022-03-25 15:09:09.000000000 +0000
@@ -75,12 +75,14 @@ let main () =
   show_environment_variable "DYLD_LIBRARY_PATH";
   show_environment_variable "DYLD_FALLBACK_LIBRARY_PATH";
   prerr_endline "Initializing library...";
-  Py.initialize ?library_name ~verbose:true ?version ?minor ();
+  Py.initialize ?library_name ~verbose:true ?version ?minor ~debug_build:true ();
   begin
     match Py.get_library_filename () with
       None -> prerr_endline "No library has been loaded.\n"
     | Some filename -> Printf.eprintf "Library \"%s\" has been loaded.\n" filename
   end;
+  Format.eprintf "platform: %s@." (Pywrappers.py_getplatform ());
+  Format.eprintf "build info: %s@." (Pywrappers.py_getbuildinfo ());
   if Py.is_debug_build () then
     prerr_endline "Debug build."
   else
diff -pruN 20211015-1/pyml_tests_common.mli 20220325-1/pyml_tests_common.mli
--- 20211015-1/pyml_tests_common.mli	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyml_tests_common.mli	2022-03-25 15:09:09.000000000 +0000
@@ -9,4 +9,6 @@ val use_version: (int option * int optio
 
 val enable_only_on_unix: ('a -> status) -> 'a -> status
 
+val launch_tests : unit -> unit
+
 val main: unit -> unit
diff -pruN 20211015-1/pyml_tests.ml 20220325-1/pyml_tests.ml
--- 20211015-1/pyml_tests.ml	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyml_tests.ml	2022-03-25 15:09:09.000000000 +0000
@@ -168,8 +168,12 @@ let () =
     (fun () ->
       let m = Py.Import.add_module "test" in
       let traceback = [
-        {Py.Traceback.filename = "file1.ml"; function_name = "func1"; line_number = 1};
-        {Py.Traceback.filename = "file2.ml"; function_name = "func2"; line_number = 2}
+        { Py.Traceback.filename = "file1.ml";
+          function_name = "func1";
+          line_number = 1};
+        { Py.Traceback.filename = "file2.ml";
+          function_name = "func2";
+          line_number = 2}
       ] in
       let mywrap _ =
         raise (Py.Err_with_traceback (Py.Err.Exception, "Great", traceback)) in
@@ -687,5 +691,15 @@ let () =
       Pyml_tests_common.Passed)
 
 let () =
+  Pyml_tests_common.add_test
+    ~title:"serialize"
+    (fun () ->
+      let value = Py.String.of_string "hello" in
+      let pickled = Marshal.to_string value [] in
+      let unpickled = Marshal.from_string pickled 0 in
+      assert (Py.String.to_string unpickled = "hello");
+      Pyml_tests_common.Passed)
+
+let () =
   if not !Sys.interactive then
     Pyml_tests_common.main ()
diff -pruN 20211015-1/pyutils.ml 20220325-1/pyutils.ml
--- 20211015-1/pyutils.ml	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyutils.ml	2022-03-25 15:09:09.000000000 +0000
@@ -4,11 +4,6 @@ let option_find f x =
   try Some (f x)
   with Not_found -> None
 
-let option_unwrap option =
-  match option with
-    None -> raise Not_found
-  | Some result -> result
-
 let substring_between string before after =
   String.sub string before (after - before)
 
@@ -39,10 +34,6 @@ let trim_carriage_return line =
   else
     line
 
-let has_prefix prefix s =
-  let prefix_length = String.length prefix in
-  String.length s >= prefix_length && String.sub s 0 prefix_length = prefix
-
 let input_lines channel =
   let accu = ref [] in
   try
@@ -53,15 +44,6 @@ let input_lines channel =
   with End_of_file ->
     List.rev !accu
 
-let read_and_close channel f arg =
-  try
-    let result = f arg in
-    close_in channel;
-    result
-  with e ->
-    close_in_noerr channel;
-    raise e
-
 let write_and_close channel f arg =
   try
     let result = f arg in
@@ -75,8 +57,7 @@ let with_temp_file contents f =
   let (file, channel) = Filename.open_temp_file "pyml_tests" ".py" in
   Fun.protect begin fun () ->
     write_and_close channel (output_string channel) contents;
-    let channel = open_in file in
-    read_and_close channel (f file) channel
+    Stdcompat.In_channel.with_open_bin file (f file)
   end
   ~finally:(fun () -> Sys.remove file)
 
diff -pruN 20211015-1/pyutils.mli 20220325-1/pyutils.mli
--- 20211015-1/pyutils.mli	2021-10-15 13:09:19.000000000 +0000
+++ 20220325-1/pyutils.mli	2022-03-25 15:09:09.000000000 +0000
@@ -25,9 +25,6 @@ val trim_carriage_return: string -> stri
 (** If the string ends with ['\r'], then returns the string without this
     character, else returns the whole string. *)
 
-val has_prefix: string -> string -> bool
-(** [has_prefix prefix s] returns [true] if [s] begins with [prefix]. *)
-
 val input_lines: in_channel -> string list
 (** Reads and returns all the lines from an input channel to the end of file.
     Carriage return characters are removed from the end of lines if any. *)
@@ -36,16 +33,6 @@ val option_find: ('a -> 'b) -> 'a -> 'b
 (** [option_find f x] returns [Some (f x)], or [None] if [f x] raises
     [Not_found]. *)
 
-val option_unwrap: 'a option -> 'a
-(** [option_unwrap x] returns [x'] if [x] is [Some x'], and raises
-    [Not_found] if [x] is [None]. *)
-
-val read_and_close: in_channel -> ('a -> 'b) -> 'a -> 'b
-(** [read_and_close channel f arg] calls [f arg], and returns the result of
-    [f].
-    [channel] is always closed after [f] has been called, even if [f] raises
-    an exception. *)
-
 val write_and_close: out_channel -> ('a -> 'b) -> 'a -> 'b
 (** [write_and_close channel f arg] calls [f arg], and returns the result of
     [f].
