pax_global_header00006660000000000000000000000064145205100310014501gustar00rootroot0000000000000052 comment=01ddf05ecf679f288416ea99f40f9ccbbfbfcb7c .gitignore000066400000000000000000000004751452051003100130430ustar00rootroot00000000000000_opam _build .merlin .depend *.cm[aioxt] *.cmti *.cmx[as] *.[ao] *.so *.swp *~ *.inc /generate /pyml.h /pyml_libdir.ml /pyml_compat.ml /pyml_arch.ml /pywrappers.mli /pywrappers.ml /pymlutop /pyml_tests.bytecode /numpy_tests.bytecode /pyml_tests.native /numpy_tests.native /pyops.ml /pyops.mli /pyml_arch_generate.exe .merlin000066400000000000000000000000151452051003100123300ustar00rootroot00000000000000PKG stdcompatCHANGES.md000066400000000000000000000253651452051003100124520ustar00rootroot00000000000000[*] marks changes that break compatibility with previous versions. # 2023-11-01 - Compatibility with Python 3.13 - 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 - #93, #94: Fix `Py.Object.get_attr_string`: this function now returns `None` when attribute is missing (the former version raised an exception, despite the `option` return type and contrary to what was documented). Reported by Lindsay Errington, @dlindsaye, https://github.com/thierry-martinez/pyml/issues/93 - #91, #92, #94: Better search heuristics for `python` library. Suggested by camlspotter and Et7f3. Use of `python-config`. Use of `otool -L` instead of `ldd` on Mac OS X. https://github.com/thierry-martinez/pyml/issues/91 https://github.com/thierry-martinez/pyml/issues/92 - #96: `find` functions (`Py.Object.find`, `Py.Object.find_string`, `Py.Dict.find`, `Py.Dict.find_string`, `Py.Object.find_attr_string` and `Py.Object.find_attr`) now consistently fail with `Not_found` exception, as it is said in the documentation. Functions `Py.Object.find_err`, `Py.Object.find_string_err`, `Py.Object.find_attr_err`, `Py.Object.find_attr_string_err` have been introduced for cases where keeping the underlying Python exception is preferable. For instance, `Py.Module.get` is now an alias for `Py.Object.find_attr_string_err` to keep the current behavior of failing with a Python exception. Reported by Jonathan Laurent, https://github.com/thierry-martinez/pyml/issues/96 # 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`, allowing caller to convert Numpy arrays to bigarrays without having to know the kind and the layout of the array (suggested by Lindsay Errington and Andie Sigler, https://github.com/thierry-martinez/pyml/issues/81) # 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 (inspired by the discussion https://discuss.ocaml.org/t/a-zoo-of-values-for-system/8525 initiated by Olaf Hering, with helpful comments from Daniel Bünzli, @EduardoRFS, David Allsopp, kit-ty-kate and jbeckford) - Better compatibility with Windows - Correct version detection for Python 3.10 # 2021-09-24 - Use `dune` as default build system (dunification done by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/28) This should in particular fix build problems of reverse dependencies with the byte-code compiler (reported by @nicoTolly, https://github.com/thierry-martinez/pyml/issues/62) - Handle more platforms with dune (reported by Olaf Hering, https://github.com/thierry-martinez/pyml/issues/68) - `pyutils` is no longer used by generate and is shipped with `pyml` package as it was the case with Makefile-based build system (reported by Olaf Hering, https://github.com/thierry-martinez/pyml/issues/69) - Support for raising exceptions with traceback from OCaml (implemented by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/65) - Fix soundness bug with `numpy` (reported by Richard Alligier, https://github.com/thierry-martinez/pyml/pull/65) - Fix `Py.Array.numpy` arrays on 32-bit platforms (reported by Olaf Hering, https://github.com/thierry-martinez/pyml/pull/70) - Fix soundness bug on strings with OCaml <4.06 (reported by OCaml CI) # 2021-02-26 - Compatibility with Python 3.10 (reported by Richard W.M. Jones) - `PyObject_AsCharBuffer`, `PyObject_AsReadBuffer`, `PyObject_AsWriteBuffer` bindings are marked optional as they have been removed in Python 3.10. - `Py_fopen` is optional and `Py_wfopen` is used instead if available. - More general handling of Unix architectures. (Fixed by Pino Toscano, https://github.com/thierry-martinez/pyml/pull/57) - Add `Py.Set` module for Python sets. (Added by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/58) - Fix #61: `Numpy.to_bigarray` raises an exception if source value is not a Numpy array instead of segfaulting. (Reported by Jonathan Laurent, https://github.com/thierry-martinez/pyml/issues/61) - Fix #56, #59: Add `python-config` heuristics to find Python library. (Reported by Anders Thuné and Nils Becker, https://github.com/thierry-martinez/pyml/issues/56 https://github.com/thierry-martinez/pyml/issues/59) - Fix `import_module_opt` for Python <3.6 (Reported by opam CI.) # 2020-05-18 - Fix: Add an `__iter__` method to python iterators. (Fixed by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/47) - Add `Py.Seq.{of_seq_map, to_seq_map, unsafe_to_seq_map, of_list, of_list_map}` functions. - Remove `Py.Import.cleanup`, which has been removed from Python 3.9, and was marked "for internal use only" before. (Reported by Victor Stinner, https://github.com/thierry-martinez/pyml/issues/49) - Fix: memory leak in `pyml_wrap_closure` (Fixed by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/53) - Add `Py.Module.set_docstring`, for Python >=3.5. (Added by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/54) - Fix: install `.cmx` files (Reported by Jonathan Laurent, https://github.com/thierry-martinez/pyml/issues/55) # 2020-02-22 - Fix: do not fail if GIL functions are unavailable - Fix: include `stdcompat.h` provided with stdcompat version 13 for the prototype of `caml_alloc_initialized_string`. - Fix: reference to the native plugin (`.cmxs`) in META # 2020-01-15 - Compatible with OCaml 4.10.0. - [PR 36] GC issue when registering a function with a dynamically allocated docstring. (Fixed by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/36) - [PR 34] Ensure that every function starting with "CAMLparamK" ends with "CAMLreturnX". (Fixed by Xavier Clerc, https://github.com/thierry-martinez/pyml/pull/34) - [GitHub issue #37] Fix test suite: 'list' object has no attribute 'clear' (Reported by Olaf Hering, https://github.com/thierry-martinez/pyml/issues/37) - [PR 38] Check for executable called python3. (Fixed by Olaf Hering, https://github.com/thierry-martinez/pyml/pull/38) - [PR 39] Expose is-instance and is-subclass. (Contribution by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/39) - [PR 44] Expose some GIL functions (functions from the Python C API related to the global interpreter lock. (Contribution by Laurent Mazare, https://github.com/thierry-martinez/pyml/pull/44) - Fix dynamic loading of stubs. (Fixed by Stéphane Glondu) # 2019-06-26 - Support for debug build of Python library (Suggested by Arlen Cox: https://github.com/thierry-martinez/pyml/issues/18) - Bug fix in pyml_check_symbol_available - `Py.compile` is a wrapper for the built-in function `compile` (Suggested by Dhruv Makwana: https://github.com/thierry-martinez/pyml/issues/25) - Guarantees for structural and physical equalities on `Py.Object.t` are now documented. New predicates Py.is_none, Py.is_null, Py.Bool.is_true, Py.Bool.is_false, Py.Tuple.is_empty. (Suggested by Laurent Mazare: https://github.com/thierry-martinez/pyml/pull/31) - Fix Py.Array.numpy to handle OCaml GC's moving the floatarray (Reported by Ilias Garnier: https://github.com/thierry-martinez/pyml/issues/30) # 2018-05-30 - `Py.import` is an alias for `Py.Import.import_module`. - Use `*_opt` naming convention for the functions that return an option instead of an exception: `Py.import_opt`, `Py.Object.find_opt`,... - of_seq/to_seq converters - [*] get_attr/get_attr_string now returns option type - Indexing operators (for OCaml 4.06.0 and above) defined in Pyops LICENSE000066400000000000000000000024571452051003100120620ustar00rootroot00000000000000BSD 2-Clause License Copyright (c) 2016-2021, Thierry Martinez. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. META000066400000000000000000000003741452051003100115220ustar00rootroot00000000000000description = "py.ml: OCaml bindings for Python" requires = "unix stdcompat" version = "20231101" archive(byte) = "pyml.cma numpy.cma" archive(native) = "pyml.cmxa numpy.cmxa" plugin(byte) = "pyml.cma numpy.cma" plugin(native) = "pyml.cmxs numpy.cmxs" Makefile000066400000000000000000000253331452051003100125130ustar00rootroot00000000000000PREFIX := /usr/local OCAMLFIND := ocamlfind INSTALL := install INSTALL_PROGRAM := $(INSTALL) bindir := $(PREFIX)/bin C_COMPILER := $(shell ocamlc -config | grep '^native_c_compiler:' | cut -d ' ' -f 2) EXT_LIB := $(shell ocamlc -config | grep '^ext_lib:' | cut -d ' ' -f 2) HAVE_OCAMLFIND := $(shell \ if $(OCAMLFIND) query -help >/dev/null 2>&1; then \ echo yes; \ else \ echo no; \ fi \ ) HAVE_UTOP := $(shell \ if [ "$(HAVE_OCAMLFIND)" = no ]; then \ echo no; \ elif $(OCAMLFIND) query utop >/dev/null 2>&1; then \ echo yes; \ else \ echo no; \ fi \ ) ifneq ($(MAKECMDGOALS),clean) ifneq ($(HAVE_OCAMLFIND),no) OCAMLC := $(OCAMLFIND) ocamlc ifneq ($(HAVE_OCAMLOPT),no) OCAMLOPTEXE := $(OCAMLFIND) ocamlopt endif OCAMLMKLIB := $(OCAMLFIND) ocamlmklib OCAMLMKTOP := $(OCAMLFIND) ocamlmktop OCAMLDEP := $(OCAMLFIND) ocamldep OCAMLDOC := $(OCAMLFIND) ocamldoc STDCOMPAT := $(shell $(OCAMLFIND) query stdcompat) else OCAMLC := $(shell \ if ocamlc.opt -version >/dev/null 2>&1; then \ echo ocamlc.opt; \ elif ocamlc -version >/dev/null 2>&1; then \ echo ocamlc; \ fi \ ) ifeq ($(OCAMLC),) $(error There is no OCaml compiler available in path) endif ifneq ($(HAVE_OCAMLOPT),no) OCAMLOPTEXE := $(shell \ if ocamlopt.opt -version >/dev/null 2>&1; then \ echo ocamlopt.opt; \ elif ocamlopt -version >/dev/null 2>&1; then \ echo ocamlopt; \ fi \ ) endif OCAMLMKLIB := ocamlmklib OCAMLMKTOP := ocamlmktop OCAMLDEP := ocamldep OCAMLDOC := ocamldoc STDCOMPAT := . 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),) $(error stdcompat module not found: please specify the path with STDCOMPAT=...) endif OCAMLVERSION := $(shell $(OCAMLC) -version) endif ifeq ($(HAVE_UTOP),yes) PYMLUTOP := pymlutop else PYMLUTOP := endif ifeq ($(OCAMLOPTEXE),) OCAMLOPT = $(error There is no optimizing OCaml compiler available) OCAMLCOPT := $(OCAMLC) CMOX := cmo CMAX := cma ALLOPT := TESTOPT := OCAMLPREFERREDLIBS := $(OCAMLBYTECODELIBS) else OCAMLOPT := $(OCAMLOPTEXE) OCAMLCOPT := $(OCAMLOPT) CMOX := cmx CMAX := cmxa ALLOPT := all.native TESTOPT := test.native OCAMLPREFERREDLIBS := $(OCAMLNATIVELIBS) endif ifeq (4.06.0,$(word 1,$(sort 4.06.0 $(OCAMLVERSION)))) PYOPS=pyops else PYOPS= endif MODULES := pyml_arch pyutils pytypes pywrappers py pycaml $(PYOPS) VERSION := $(shell date "+%Y%m%d") OCAMLLIBFLAGS := -cclib "-L. -lpyml_stubs" OCAMLLIBNUMPYFLAGS := -cclib "-L. -lnumpy_stubs" OCAMLLIBFLAGSNATIVE := $(OCAMLLIBFLAGS) OCAMLLIBFLAGSBYTECODE := -custom $(OCAMLLIBFLAGS) INSTALL_FILES := \ py.mli numpy.mli $(MODULES:=.cmi) $(MODULES:=.cmx) \ numpy.cmi \ pyml.cma pyml.cmxa pyml.cmxs pyml$(EXT_LIB) \ numpy.cma numpy.cmxa numpy.cmxs numpy$(EXT_LIB) \ $(MODULES:=.cmx) numpy.cmx \ libpyml_stubs$(EXT_LIB) dllpyml_stubs.so \ libnumpy_stubs$(EXT_LIB) dllnumpy_stubs.so \ META .PHONY : all all : all.bytecode $(ALLOPT) @echo The py.ml library is compiled. @echo Run \`make doc\' to build the documentation. @echo Run \`make test\' to check the test suite. ifneq ($(HAVE_OCAMLFIND),no) @echo Run \`make install\' to install the library via ocamlfind. endif @echo Run \`make pymltop\' to build the toplevel. ifneq ($(HAVE_UTOP),no) @echo Run \`make pymlutop\' to build the utop toplevel. endif .PHONY : help help : @echo make [all] : build the library @echo make all.bytecode : build only the bytecode library @echo make all.native : build only the native library @echo make doc : build the documentation ifneq ($(HAVE_OCAMLFIND),no) @echo make install : install the library via ocamlfind endif @echo make clean : remove all the generated files @echo make tests : compile and run the test suite @echo make tests.bytecode : run only the bytecode version of the tests @echo make tests.native : run only the native version of the tests @echo make pymltop: build the toplevel ifneq ($(HAVE_UTOP),no) @echo make pymlutop: build the utop toplevel. endif @echo make HAVE_OCAMLFIND=no : disable ocamlfind @echo make HAVE_OCAMLOPT=no : disable ocamlopt @echo \ "make OCAMLC|OCAMLOPT|OCAMLMKLIB|OCAMLMKTOP|OCAMLDEP|OCAMLDOC=... :" @echo " set paths to OCaml tools" @echo make OCAMLCFLAGS=... : set flags to OCaml compiler for compiling @echo make OCAMLLDFLAGS=... : set flags to OCaml compiler for linking @echo make OCAMLLIBFLAGS=... : @echo " set flags to OCaml compiler for building the library" @echo make STDCOMPAT=... : set path to the stdcompat library .PHONY : all.bytecode all.bytecode : pyml.cma numpy.cma .PHONY : all.native all.native : pyml.cmxa pyml.cmxs numpy.cmxa numpy.cmxs .PHONY : test test : test.bytecode $(TESTOPT) .PHONY : test.bytecode test.bytecode : pyml_tests.bytecode numpy_tests.bytecode ./pyml_tests.bytecode $(TEST_OPTIONS) ./numpy_tests.bytecode $(TEST_OPTIONS) .PHONY : test.native test.native : pyml_tests.native numpy_tests.native ./pyml_tests.native $(TEST_OPTIONS) ./numpy_tests.native $(TEST_OPTIONS) .PHONY : install install : $(INSTALL_FILES) ifeq ($(HAVE_OCAMLFIND),no) $(error ocamlfind is needed for 'make install') endif $(OCAMLFIND) install pyml $(INSTALL_FILES) [ ! -f pymltop ] || $(INSTALL_PROGRAM) pymltop $(bindir)/pymltop [ ! -f pymlutop ] || $(INSTALL_PROGRAM) pymlutop $(bindir)/pymlutop .PHONY : uninstall uninstall : $(OCAMLFIND) remove pyml - rm $(bindir)/pymltop - rm $(bindir)/pymlutop .PHONY : clean clean : for module in $(MODULES) numpy generate pyml_tests_common pyml_tests \ numpy_tests; do \ rm -f $$module.cmi $$module.cmo $$module.cmx $$module$(EXT_LIB) \ $$module.o; \ done rm -f pyml.cma pyml.cmxa pyml.cmxs pyml$(EXT_LIB) rm -f numpy.cma numpy.cmxa numpy.cmxs numpy$(EXT_LIB) rm -f pywrappers.mli pywrappers.ml pyml_dlsyms.inc pyml_wrappers.inc 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.ml rm -f generate pyml_tests.native pyml_tests.bytecode rm -f numpy_tests.native numpy_tests.bytecode rm -f .depend rm -rf doc rm -f pymltop pytop.cmo pymlutop pyutop.cmo rm -f pymltop_libdir.ml pymltop_libdir.cmo rm -f pyops.mli pyops.ml .PHONY : tarball tarball : git archive --format=tar.gz --prefix=pyml-$(VERSION)/ HEAD \ >pyml-$(VERSION).tar.gz doc : py.mli pycaml.mli numpy.mli pywrappers.ml mkdir -p $@ $(OCAMLDOC) $(OCAMLCFLAGS) -html -d $@ $^ touch $@ .depend : $(MODULES:=.ml) $(MODULES:=.mli) numpy.ml numpy.mli \ pyml_tests_common.mli pyml_tests_common.ml pyml_tests.ml numpy_tests.ml $(OCAMLDEP) $^ >$@ ifneq ($(MAKECMDGOALS),clean) -include .depend endif pyutils.cmo pyutils.cmx : pyutils.cmi generate : pyutils.$(CMOX) generate.$(CMOX) $(OCAMLCOPT) $(OCAMLLDFLAGS) $(OCAMLPREFERREDLIBS) $^ -o $@ generate.cmo : generate.ml generate.cmx : generate.ml pywrappers.ml pyml_wrappers.inc : generate ./generate pyml_wrappers.inc : pywrappers.ml pywrappers.mli : pywrappers.ml pytypes.cmi pyml_arch.cmi $(OCAMLC) $(OCAMLCFLAGS) -i $< >$@ pyml_tests.native : py.cmi pyml.cmxa pyml_tests_common.cmx pyml_tests.cmx $(OCAMLOPT) $(OCAMLLDFLAGS) $(OCAMLNATIVELIBS) pyml.cmxa \ pyml_tests_common.cmx pyml_tests.cmx -o $@ pyml_tests.bytecode : py.cmi pyml.cma pyml_tests_common.cmo pyml_tests.cmo $(OCAMLC) $(OCAMLLDFLAGS) $(OCAMLBYTECODELIBS) pyml.cma \ pyml_tests_common.cmo pyml_tests.cmo -o $@ numpy_tests.native : py.cmi pyml.cmxa numpy.cmxa \ pyml_tests_common.cmx numpy_tests.cmx $(OCAMLOPT) $(OCAMLLDFLAGS) $(OCAMLNATIVELIBSNUMPY) \ pyml.cmxa numpy.cmxa \ pyml_tests_common.cmx numpy_tests.cmx -o $@ numpy_tests.bytecode : py.cmi pyml.cma numpy.cma \ pyml_tests_common.cmo numpy_tests.cmo $(OCAMLC) $(OCAMLLDFLAGS) $(OCAMLBYTECODELIBSNUMPY) pyml.cma \ numpy.cma pyml_tests_common.cmo numpy_tests.cmo -o $@ pyml_arch.ml : pyml_arch.ml.c $(C_COMPILER) -E $< | sed '/^#/d' >$@ pyml_arch.cmo pyml_arch.cmx : pyml_arch.cmi %.cmi : %.mli $(OCAMLC) $(OCAMLCFLAGS) -c $< -o $@ %.cmo : %.ml $(OCAMLC) $(OCAMLCFLAGS) -c $< -o $@ %.cmx : %.ml $(OCAMLOPT) $(OCAMLCFLAGS) -c $< -o $@ %.o : %.c $(OCAMLC) $(OCAMLCFLAGS) -c $< -o $@ pyml_stubs.o : pyml_wrappers.inc pyml.cma : $(MODULES:=.cmo) libpyml_stubs$(EXT_LIB) $(OCAMLC) $(OCAMLLIBFLAGSBYTECODE) -a -dllib -lpyml_stubs $(MODULES:=.cmo) -o $@ pyml.cmxa : $(MODULES:=.cmx) libpyml_stubs$(EXT_LIB) $(OCAMLOPT) $(OCAMLLIBFLAGSNATIVE) -a $(MODULES:=.cmx) -o $@ pyml.cmxs : $(MODULES:=.cmx) libpyml_stubs$(EXT_LIB) $(OCAMLOPT) $(OCAMLLIBFLAGSNATIVE) -shared $(MODULES:=.cmx) -o $@ lib%$(EXT_LIB) : %.o $(OCAMLMKLIB) -o $(basename $<) $< numpy.cma : numpy.cmo libnumpy_stubs$(EXT_LIB) $(OCAMLC) $(OCAMLLIBNUMPYFLAGS) -a -dllib -lnumpy_stubs numpy.cmo -o $@ numpy.cmxa : numpy.cmx libnumpy_stubs$(EXT_LIB) $(OCAMLOPT) $(OCAMLLIBNUMPYFLAGS) -a numpy.cmx -o $@ numpy.cmxs : numpy.cmx libnumpy_stubs$(EXT_LIB) $(OCAMLOPT) $(OCAMLLIBNUMPYFLAGS) -shared numpy.cmx -o $@ pytop.cmo : pytop.ml pymltop_libdir.cmi $(OCAMLC) -I +compiler-libs -c $< pymltop_libdir.ml : if [ -z "$(PREFIX)" ]; then \ echo "let libdir=\"$(PWD)\""; \ else \ echo "let libdir=\"$(PREFIX)/lib/pyml/\""; \ fi >$@ pymltop : pyml.cma numpy.cma pymltop_libdir.cmo pytop.cmo $(OCAMLMKTOP) $(OCAMLLDFLAGS) $(OCAMLLIBNUMPYFLAGS) $(OCAMLBYTECODELIBSNUMPY) $^ -o $@ pyutop.cmo : pyutop.ml ifeq ($(HAVE_OCAMLFIND),no) $(error ocamlfind is needed for utop) endif $(OCAMLC) $(OCAMLCFLAGS) -thread -package utop -c $< -o $@ pymlutop : pyml.cma numpy.cma pymltop_libdir.cmo pytop.cmo pyutop.cmo ifeq ($(HAVE_OCAMLFIND),no) $(error ocamlfind is needed for utop) endif # ocamlmktop raises "Warning 31". See https://github.com/diml/utop/issues/212 # $(OCAMLMKTOP) -o $@ -thread -linkpkg -package utop -dontlink compiler-libs $^ ocamlfind ocamlc -thread -linkpkg -linkall -predicates create_toploop \ -package compiler-libs.toplevel,utop,stdcompat $^ -o $@ pyops.ml: pyops.ml.new cp $< $@ pyops.mli: pyops.mli.new cp $< $@ README000066400000000000000000000030351452051003100117260ustar00rootroot00000000000000py.ml: OCaml bindings for Python py.ml provides OCaml bindings for Python 2 and Python 3. This library subsumes the pycaml library, which is no longer actively maintained. Homepage: http://pyml.gforge.inria.fr Documentation: http://pyml.gforge.inria.fr/doc Git: git clone http://pyml.gforge.inria.fr/pyml.git Git Repository Browser: http://pyml.gforge.inria.fr/browser Tracker for bug reports and feature requests: http://pyml.gforge.inria.fr/tracker OPAM: opam install pyml The Python library is linked at runtime and the same executable can be run in a Python 2 or a Python 3 environment. py.ml does not require any Python library at compile time. The only compile time dependency is Stdcompat to ensure compatibility with all OCaml compiler versions from 3.12: https://github.com/thierry-martinez/stdcompat/ Bindings are split in three modules: - Py provides the initialization functions and some high-level bindings, with error handling and naming conventions closer to OCaml usages. - Pycaml provides a signature close to the old Pycaml module, so as to ease migration. - Pywrappers provides low-level bindings, which follow closely the conventions of the C bindings for Python. Submodules Pywrappers.Python2 and Pywrappers.Python3 contain version-specific bindings. Custom top-level A custom top-level with the C bindings can be compiled by make pymltop. If you have utop and ocamlfind, you can make pymlutop. For OPAM users: pymltop is installed by default by opam install pyml. pymlutop is installed whenever utop is available. README.md000066400000000000000000000276401452051003100123350ustar00rootroot00000000000000``py.ml``: OCaml bindings for Python ==================================== ``py.ml`` provides OCaml bindings for Python 2 and Python 3. This library subsumes the ``pycaml`` library, which is no longer actively maintained. *OPAM:* ``opam install pyml`` The Python library is linked at runtime and the same executable can be run in a Python 2 or a Python 3 environment. ``py.ml`` does not require any Python library at compile time. The only compile time dependency is [``Stdcompat``](https://github.com/thierry-martinez/stdcompat) to ensure compatibility with all OCaml compiler versions from 3.12. Bindings are split in three modules: - ``Py`` provides the initialization functions and some high-level bindings, with error handling and naming conventions closer to OCaml usages. - ``Pycaml`` provides a signature close to the old ``Pycaml`` module, so as to ease migration. - ``Pywrappers`` provides low-level bindings, which follow closely the conventions of the C bindings for Python. Submodules ``Pywrappers.Python2`` and ``Pywrappers.Python3`` contain version-specific bindings. Custom top-level ---------------- A custom top-level with the C bindings can be compiled by ``make pymltop``. If you have ``utop`` and ``ocamlfind``, you can ``make pymlutop``. *For OPAM users:* ``pymltop`` is installed by default by ``opam install pyml``. ``pymlutop`` is installed whenever ``utop`` is available. Getting started --------------- ``Py.initialize ()`` loads the Python library. ``Py.Run.simple_string "print('Hello, world!')"`` executes a Python phrase and returns ``true`` if the execution succeeded, ``false`` otherwise. ``Py.Run.eval "18 + 42"`` evaluates a Python phrase and returns a value of type ``Py.Object.t``. Such a value can then be converted to an OCaml value: ``assert (Py.Int.to_int (Py.Run.eval "18 + 42") = 60)``. In case of error (either the phrase is syntactically incorrect or the evaluation raises an exception), an OCaml exception ``Py.E (type, msg)`` is raised. By default, ``Py.Run.eval`` evaluates an expression; use ``Py.Run.eval ~start:Py.File`` to evaluate a Python phrase as a module/file (with ``import`` directives and so on). To make an OCaml value accessible from Python, we create a module (called ``ocaml`` in this example, but it can be any valid Python module name): ```ocaml let m = Py.Import.add_module "ocaml" in Py.Module.set m "example_value" (Py.List.of_list_map Py.Int.of_int [1;2;3]); Py.Run.eval ~start:Py.File " from ocaml import example_value print(example_value)" ``` OCaml functions can be passed in the same way. ``` ocaml let m = Py.Import.add_module "ocaml" in let hello args = Printf.printf "Hello, %s!\n" (Py.String.to_string args.(0)); Py.none in Py.Module.set m "hello" (Py.Callable.of_function hello); Py.Run.eval ~start:Py.File " from ocaml import hello hello('World')" ``` ``Py.Module.set m "hello" (Py.Callable.of_function hello)`` can be written ``Py.Module.set_function m "hello" hello``. Python functions can be called from OCaml too. ```ocaml let builtins = Py.Eval.get_builtins () in let sorted_python = Py.Dict.find_string builtins "sorted" in let sorted = Py.Callable.to_function sorted_python in let result = sorted [| Py.List.of_list_map Py.Float.of_float [3.0; 2.0] |] in assert (Py.List.to_list_map Py.Float.to_float result = [2.0; 3.0]) ``` ``Py.Run.interactive ()`` runs the Python top-loop. It can be run after some OCaml values have been made accessible through a module as above. If IPython is available, the top-loop can be run with ``Py.Run.ipython ()``. With OCaml 4.06 and greater, the module ``Pyops`` declares indexing operators. | Indexing operator | Getter | Setter | |-------------------|--------|--------| | ``x.@(v)`` | ``Py.Object.find_attr`` | ``Py.Object.set_attr`` | | ``x.@$(v)`` | ``Py.Object.find_attr_string`` / ``Py.Module.get`` | ``Py.Object.set_attr_string`` / ``Py.Module.set `` | | ``x.![v]`` | ``Py.Object.find`` | ``Py.Object.set_item`` | | ``x.!$[v]`` | ``Py.Object.find_string`` | ``Py.Object.set_item_string`` | | ``x.%[v]`` | ``Py.Dict.find`` | ``Py.Dict.set_item`` | | ``x.%$[v]`` | ``Py.Dict.find_string`` | ``Py.Dict.set_item_string`` | | ``x.&(v)`` | ``Py.Module.get_function`` | ``Py.Module.set_function`` | The "hello world" example above can be written: ``` ocaml let m = Py.Import.add_module "ocaml" in let open Pyops in m.&("hello") <- (fun args -> Printf.printf "Hello, %s!\n" (Py.String.to_string args.(0)); Py.none); Py.Run.eval ~start:Py.File " from ocaml import hello hello('World')" ``` Error handling -------------- All Python exceptions are caught as OCaml exceptions ``Py.E (type, msg)``, where ``type`` and ``msg`` are two Python objects. Typically, one can convert ``type`` and ``msg`` to strings with ``Py.Object.to_string`` to display or analyse them. When an OCaml function ``f`` is called from Python (passed by ``Py.Callable.of_function``), ``f`` can raise a Python exception by raising an OCaml exception of the form ``Py.E (type, msg)``. To raise standard errors more conveniently, ``f`` can raise an exception of the form ``Py.Err (type, msg)`` instead, 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 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 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 ---------- Python values have type ``Py.Object.t``. ``Py.String.of_string`` and ``Py.String.to_string`` convert back and forth OCaml to Python strings: Unicode strings and legacy strings are handled uniformly. ``Py.Int.of_int`` and ``Py.Int.to_int`` convert back and forth OCaml to Python integers. For big numbers, one should use an intermediate textual representation with ``Py.Int.of_string`` and ``Py.Int.to_string``. ``Py.Float.of_float`` and ``Py.Float.to_float`` convert back and forth OCaml to Python floating-point values. The module `Py.Number` define common arithmetic and bitwise operations on Python numbers. It can be open locally to take benefit from operator overloading. E.g.: ``` ocaml let m = Py.Import.add_module "ocaml" in let square args = Py.Number.(args.(0) ** of_int 2) in Py.Module.set_function m "square" square; ignore (Py.Run.eval ~start:Py.File " from ocaml import square print(square(3))") ``` ``Py.Tuple.of_array`` and ``Py.Tuple.to_array`` can be used to construct and destruct Python tuples. ``Py.Tuple.of_list`` and ``Py.Tuple.to_list`` are available as well. The empty tuple is ``Py.Tuple.empty``. Converters can be composed with the ``_map`` suffix: for example, ``Py.Tuple.of_list_map Py.Int.of_int`` converts a list of integers to a Python value. Short tuples can be constructed and destructed with ``Py.Tuple.of_tuple1``, ..., ``Py.Tuple.of_tuple5`` and ``Py.Tuple.to_tuple1``, ..., ``Py.Tuple.to_tuple5``. For Python lists, ``Py.List.of_array``, ``Py.List.to_array``, ``Py.List.of_list`` and ``Py.List.to_list`` are available (along with their ``_map`` variant). ``Py.List`` and ``Py.Tuple`` include the ``Py.Sequence`` module: along with ``to_array`` and ``to_list``, iterators like ``fold_left`` ``fold_right``, ``for_all``, etc. are available. Python iterators can be iterated with ``Py.Iter.next`` which returns an option value, where ``None`` represents the end of the iteration. ``Py.Iter.iter``, ``Py.Iter.fold_left``,``Py.Iter.fold_right`` and ``Py.Iter.to_list`` can be used as well. A Python iterator can be created with ``Py.Iter.create`` from an OCaml function that returns an option. Python dictionaries can be constructed and destructed with ``Py.Dict.of_bindings_string`` and ``Py.Dict.to_bindings_string`` from and to associative lists between string keys and Python values. ``Py.Dict.find_string`` can be used to find a single value (the exception ``Not_found`` is raised if the key is not in the dictionary; ``Py.Dict.get_item_string`` provides the option variant). Python closures can be called from OCaml with ``Py.Callable.to_function``, or ``Py.Callable.to_function_with_keywords`` to pass keywords as an associative list. Symmetrically, OCaml functions can be turned into Python closures with ``Py.Callable.of_function`` and ``Py.Callable.of_function_with_keywords`` (the latter function passes keywords as a dictionary to the OCaml callback: values can be retrieved efficiently with ``Py.Dict.find_string``). ```ocaml let m = Py.Import.add_module "ocaml" in Py.Module.set_function_with_keywords m "length" (fun args kw -> let x = Py.Dict.find_string kw "x" in let y = Py.Dict.find_string kw "y" in Py.Number.((x ** of_int 2 + y ** of_int 2) ** of_float 0.5)); ignore (Py.Run.eval ~start:Py.File " from ocaml import length print(length(x=3, y=4))") ``` Modules ------- New modules can be defined with ``Py.Import.add_module`` and existing modules can be imported with ``Py.Import.import_module`` (or the shorter ``Py.import`` alias). Trying to import a module that does not exist leads to a Python exception: use ``Py.Import.import_module_opt`` to get an option result instead (or the shorter ``Py.import_opt`` alias). ``Module.get`` and ``Module.set`` allow to retrieve and define module members. For function members, there are shortcuts to do the conversion with ``Py.Callable``: ``Module.get_function`` and ``Module.set_function`` (and ``Module.get_function_with_keywords`` and ``Module.set_function_with_keywords``). If we consider the following Python code taken from ``matplotlib`` documentation: ```python import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 5, 0.1); y = np.sin(x) plt.plot(x, y) plt.show() ``` The code can be written directly in OCaml as such: ```ocaml let np = Py.import "numpy" in let plt = Py.import "matplotlib.pyplot" in let x = Py.Module.get_function np "arange" (Array.map Py.Float.of_float [| 0.; 5.; 0.1 |]) in let y = Py.Module.get_function np "sin" [| x |] in ignore (Py.Module.get_function plt "plot" [| x; y |]); assert (Py.Module.get_function plt "show" [| |] = Py.none) ``` or, using indexing operators (OCaml 4.06): ```ocaml let np = Py.import "numpy" in let plt = Py.import "matplotlib.pyplot" in let open Pyops in let x = np.&("arange")(Array.map Py.Float.of_float [| 0.; 5.; 0.1 |]) in let y = np.&("sin")[| x |] in ignore (plt.&("plot")[| x; y |]); assert (plt.&("show")[| |] = Py.none) ``` NumPy ----- If the NumPy library is installed, then OCaml float arrays and bigarrays can be shared in place with Python code as NumPy arrays (without copy). Python code can then directly read and write from and to the OCaml arrays and changes are readable from OCaml. ```ocaml let array = [| 1.; 2. ; 3. |] in let m = Py.Import.add_module "ocaml" in Py.Module.set m "array" (Py.Array.numpy array); ignore (Py.Run.eval ~start:Py.File " from ocaml import array array *= 2"); assert (array = [| 2.; 4.; 6. |]) ``` Bigarrays are handled by the ``Numpy`` module that is shipped in ``numpy.cma/cmxa`` and requires ``bigarray.cma/cmxa``. Numpy arrays can be obtained from bigarrays with ``Numpy.of_bigarray`` and bigarrays can be obtained from Numpy arrays with ``Numpy.to_bigarray`` (the provided kind and layout should match the format of the Numpy array). ```ocaml let m = Py.Import.add_module "test" in let callback arg = let bigarray = Numpy.to_bigarray Bigarray.nativeint Bigarray.c_layout arg.(0) in let array1 = Bigarray.array1_of_genarray bigarray in assert (Bigarray.Array1.get array1 0 = 0n); assert (Bigarray.Array1.get array1 1 = 1n); assert (Bigarray.Array1.get array1 2 = 2n); assert (Bigarray.Array1.get array1 3 = 3n); Py.none in Py.Module.set m "callback" (Py.Callable.of_function callback); assert (Py.Run.simple_string " from test import callback import numpy callback(numpy.array([0,1,2,3])) ") ``` dune000066400000000000000000000027571452051003100117360ustar00rootroot00000000000000(library (public_name pyml) (modules numpy py pyops pycaml pyml_arch pytypes pywrappers pyutils) (foreign_stubs (language c) (names numpy_stubs pyml_stubs)) (wrapped false) (libraries unix bigarray stdcompat)) (executables (names generate) (modules generate) (libraries stdcompat)) (rule (targets pywrappers.ml pyml.h pyml_dlsyms.inc pyml_wrappers.inc) (deps (:gen generate.exe)) (action (run %{gen}))) (rule (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 pyml_arch.ml.sharp) (action (with-stdout-to %{target} (run sed "/^#/d" %{deps})))) (library (name pyml_tests_common) (modules pyml_tests_common) (libraries pyml stdcompat)) (test (name numpy_tests) (modules numpy_tests) (libraries pyml pyml_tests_common stdcompat)) (test (name pyml_tests) (modules pyml_tests) (libraries pyml pyml_tests_common stdcompat)) (rule (enabled_if (>= %{ocaml_version} 4.06)) (target pyops.mli) (deps pyops.mli.new) (action (copy %{deps} %{target}))) (rule (enabled_if (>= %{ocaml_version} 4.06)) (target pyops.ml) (deps pyops.ml.new) (action (copy %{deps} %{target}))) (rule (enabled_if (< %{ocaml_version} 4.06)) (target pyops.mli) (deps pyops.mli.405) (action (copy %{deps} %{target}))) (rule (enabled_if (< %{ocaml_version} 4.06)) (target pyops.ml) (deps pyops.ml.405) (action (copy %{deps} %{target}))) dune-project000066400000000000000000000012131452051003100133640ustar00rootroot00000000000000(lang dune 2.8) (name pyml) (license BSD-2-Clause) (maintainers "Thierry Martinez ") (authors "Thierry Martinez ") (source (github thierry-martinez/pyml)) (bug_reports "http://github.com/thierry-martinez/pyml/issues") (homepage "http://github.com/thierry-martinez/pyml") (documentation "http://github.com/thierry-martinez/pyml") (generate_opam_files true) (package (name pyml) (synopsis "OCaml bindings for Python") (description "OCaml bindings for Python 2 and Python 3") (depends (ocaml (>= 3.12.1)) (ocamlfind :build) (stdcompat (>= 18)) (conf-python-3-dev :with-test)) (depopts utop)) generate.ml000066400000000000000000001614041452051003100131770ustar00rootroot00000000000000open Stdcompat type ty = PyObject of bool | PyCompilerFlags | String | WideString | Int | Int64 | Long | Size | IntPtr | Compare | Input | Unit | FileIn of bool | FileOut of bool | Double | StringOption | NeverReturn | UCS2 | UCS4 | UCS2Option | UCS4Option of bool type arguments = Value | Deref | Fun of ty list type wrapper = { symbol: string; arguments: arguments; result: ty; optional: bool; } let wrappers = [{ symbol = "_Py_NoneStruct"; arguments = Value; result = PyObject false; optional = false; }; { symbol = "_Py_TrueStruct"; arguments = Value; result = PyObject false; optional = false; }; { symbol = "Py_Exit"; arguments = Fun [Int]; result = NeverReturn; optional = false; }; { symbol = "Py_GetVersion"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_GetPlatform"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_GetCopyright"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_GetCompiler"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_GetBuildInfo"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_FdIsInteractive"; arguments = Fun [FileIn true; String]; result = Int; optional = false; }; { symbol = "Py_Initialize"; arguments = Fun []; result = Unit; optional = false; }; { symbol = "PyBool_Type"; arguments = Value; result = PyObject false; optional = false; }; { symbol = "PyCapsule_Type"; arguments = Value; result = PyObject false; optional = false; }; { symbol = "PyCallable_Check"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyDict_Clear"; arguments = Fun [PyObject false]; result = Unit; optional = false; }; { symbol = "PyDict_Copy"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyDict_DelItem"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyDict_DelItemString"; arguments = Fun [PyObject false; String]; result = Int; optional = false; }; { symbol = "PyDict_GetItem"; arguments = Fun [PyObject false; PyObject false]; result = PyObject false; optional = false; }; { symbol = "PyDict_GetItemString"; arguments = Fun [PyObject false; String]; result = PyObject false; optional = false; }; { symbol = "PyDict_Keys"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyDict_Items"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyDict_New"; arguments = Fun []; result = PyObject true; optional = false; }; { symbol = "PyDict_SetItem"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyDict_SetItemString"; arguments = Fun [PyObject false; String; PyObject false]; result = Int; optional = false; }; { symbol = "PyDict_Size"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyDict_Values"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyErr_Clear"; arguments = Fun []; result = Unit; optional = false; }; { symbol = "PyErr_ExceptionMatches"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyErr_GivenExceptionMatches"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyErr_Occurred"; arguments = Fun []; result = PyObject false; optional = false; }; { symbol = "PyErr_Print"; arguments = Fun []; result = Unit; optional = false; }; { symbol = "PyErr_PrintEx"; 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; optional = false; }; { symbol = "PyErr_SetString"; arguments = Fun [PyObject false; String]; result = Unit; optional = false; }; { symbol = "PyErr_SetObject"; arguments = Fun [PyObject false; PyObject false]; result = Unit; optional = false; }; { symbol = "PyEval_CallObjectWithKeywords"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyEval_GetBuiltins"; arguments = Fun []; result = PyObject false; optional = false; }; { symbol = "PyEval_GetGlobals"; arguments = Fun []; result = PyObject false; optional = false; }; { symbol = "PyEval_GetLocals"; arguments = Fun []; result = PyObject false; optional = false; }; { symbol = "PyExc_BaseException"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_Exception"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_StopIteration"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_GeneratorExit"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_ArithmeticError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_LookupError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_AssertionError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_AttributeError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_BufferError"; 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; optional = false; }; { symbol = "PyExc_FloatingPointError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_OSError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_ImportError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_IndexError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_KeyError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_KeyboardInterrupt"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_MemoryError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_NameError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_OverflowError"; 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; optional = false; }; { symbol = "PyExc_NotImplementedError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_SyntaxError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_IndentationError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_TabError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_ReferenceError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_SystemError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_SystemExit"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_TypeError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_UnboundLocalError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_UnicodeError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_UnicodeEncodeError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_UnicodeDecodeError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_UnicodeTranslateError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_ValueError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_ZeroDivisionError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_EnvironmentError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_IOError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_RecursionErrorInst"; arguments = Deref; result = PyObject false; optional = true; }; { symbol = "PyExc_Warning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_UserWarning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_DeprecationWarning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_PendingDeprecationWarning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_SyntaxWarning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_RuntimeWarning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_FutureWarning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_ImportWarning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_UnicodeWarning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyExc_BytesWarning"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyFloat_AsDouble"; arguments = Fun [PyObject false]; result = Double; optional = false; }; { symbol = "PyFloat_FromDouble"; arguments = Fun [Double]; result = PyObject true; optional = false; }; { symbol = "PyFloat_Type"; arguments = Value; result = PyObject false; optional = false; }; { symbol = "PyGILState_Check"; arguments = Fun []; result = Int; optional = true; }; { symbol = "PyGILState_Ensure"; arguments = Fun []; result = Int; optional = true; }; { symbol = "PyGILState_Release"; arguments = Fun [Int]; result = Unit; optional = true; }; { symbol = "PyImport_AddModule"; arguments = Fun [String]; result = PyObject false; optional = false; }; { symbol = "PyImport_Cleanup"; arguments = Fun []; result = Unit; optional = true; }; { symbol = "PyImport_ExecCodeModule"; arguments = Fun [String; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyImport_ExecCodeModuleEx"; arguments = Fun [String; PyObject false; String]; result = PyObject true; optional = false; }; { symbol = "PyImport_GetMagicNumber"; arguments = Fun []; result = Int64; optional = false; }; { symbol = "PyImport_GetModuleDict"; arguments = Fun []; result = PyObject false; optional = false; }; { symbol = "PyImport_ImportFrozenModule"; arguments = Fun [String]; result = Int; optional = false; }; { symbol = "PyImport_Import"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyImport_ImportModule"; arguments = Fun [String]; result = PyObject true; optional = false; }; { symbol = "PyImport_ImportModuleLevel"; arguments = Fun [ String; PyObject false; PyObject false; PyObject false; Int]; result = PyObject true; optional = false; }; { symbol = "PyImport_ReloadModule"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyIter_Next"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyList_New"; arguments = Fun [Size]; result = PyObject true; optional = false; }; { symbol = "PyList_GetItem"; arguments = Fun [PyObject false; Size]; result = PyObject false; optional = false; }; { symbol = "PyList_SetItem"; arguments = Fun [PyObject false; Int; PyObject true]; result = Int; optional = false; }; { symbol = "PyList_Size"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyLong_AsLong"; arguments = Fun [PyObject false]; result = Int64; optional = false; }; { symbol = "PyLong_FromLong"; arguments = Fun [Int64]; result = PyObject true; optional = false; }; { symbol = "PyMapping_Check"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyMapping_GetItemString"; arguments = Fun [PyObject false; String]; result = PyObject true; optional = false; }; { symbol = "PyMapping_HasKey"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyMapping_HasKeyString"; arguments = Fun [PyObject false; String]; result = Int; optional = false; }; { symbol = "PyMapping_Length"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyMapping_SetItemString"; arguments = Fun [PyObject false; String; PyObject false]; result = Int; optional = false; }; { symbol = "PyMapping_Size"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyMarshal_ReadObjectFromFile"; arguments = Fun [FileIn true]; result = PyObject true; optional = false; }; { symbol = "PyMarshal_ReadLastObjectFromFile"; arguments = Fun [FileIn true]; result = PyObject true; optional = false; }; { symbol = "PyMarshal_ReadObjectFromString"; arguments = Fun [String; Size]; result = PyObject true; optional = false; }; { symbol = "PyMarshal_WriteObjectToFile"; arguments = Fun [PyObject false; FileOut true; Int]; result = Unit; optional = false; }; { symbol = "PyMarshal_WriteObjectToString"; arguments = Fun [PyObject false; Int]; result = PyObject true; optional = false; }; { symbol = "PyMethod_Function"; arguments = Fun [PyObject false]; result = PyObject false; optional = false; }; { symbol = "PyMethod_New"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyMethod_Self"; arguments = Fun [PyObject false]; result = PyObject false; optional = false; }; { symbol = "PyModule_AddObject"; arguments = Fun [PyObject false; String; PyObject false]; result = Int; optional = false; }; { symbol = "PyModule_GetDict"; arguments = Fun [PyObject false]; result = PyObject false; optional = false; }; { symbol = "PyModule_GetFilename"; arguments = Fun [PyObject false]; result = StringOption; optional = false; }; { symbol = "PyModule_GetName"; arguments = Fun [PyObject false]; result = StringOption; optional = false; }; { symbol = "PyModule_New"; arguments = Fun [String]; result = PyObject true; optional = false; }; { symbol = "PyModule_Type"; arguments = Value; result = PyObject false; optional = false; }; { symbol = "PyModule_SetDocString"; arguments = Fun [PyObject false; String]; result = Int; optional = true; }; { symbol = "PyNumber_Absolute"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Add"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_And"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Check"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyNumber_Divmod"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Float"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_FloorDivide"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceAdd"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceAnd"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceFloorDivide"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceLshift"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceMultiply"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceOr"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlacePower"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceRemainder"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceRshift"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceSubtract"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceTrueDivide"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceXor"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Invert"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Long"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Lshift"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Multiply"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Negative"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Or"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Positive"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Power"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Remainder"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Rshift"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Subtract"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_TrueDivide"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Xor"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyObject_Call"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyObject_DelItem"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_DelItemString"; 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; optional = false; }; { symbol = "PyObject_GetAttrString"; arguments = Fun [PyObject false; String]; result = PyObject true; optional = false; }; { symbol = "PyObject_GetItem"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyObject_GetIter"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyObject_HasAttr"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_HasAttrString"; arguments = Fun [PyObject false; String]; result = Int; optional = false; }; { symbol = "PyObject_Hash"; arguments = Fun [PyObject false]; result = Int64; optional = false; }; { symbol = "PyObject_IsTrue"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_IsInstance"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_IsSubclass"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_Not"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_Print"; arguments = Fun [PyObject false; FileOut true; Int]; result = Int; optional = false; }; { symbol = "PyObject_Repr"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyObject_RichCompare"; arguments = Fun [PyObject false; PyObject false; Compare]; result = PyObject true; optional = false; }; { symbol = "PyObject_RichCompareBool"; arguments = Fun [PyObject false; PyObject false; Compare]; result = Int; optional = false; }; { symbol = "PyObject_SetAttr"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_SetAttrString"; arguments = Fun [PyObject false; String; PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_SetItem"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_Size"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_Str"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyObject_Type"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyRun_AnyFileExFlags"; arguments = Fun [FileIn false; String; Int; PyCompilerFlags]; result = Int; optional = false; }; { symbol = "PyRun_FileExFlags"; arguments = Fun [FileIn false; String; Input; PyObject false; PyObject false; Int; PyCompilerFlags]; result = PyObject true; optional = false; }; { symbol = "PyRun_InteractiveOneFlags"; arguments = Fun [FileIn true; String; PyCompilerFlags]; result = Int; optional = false; }; { symbol = "PyRun_InteractiveLoopFlags"; arguments = Fun [FileIn true; String; PyCompilerFlags]; result = Int; optional = false; }; { symbol = "PyRun_SimpleFileExFlags"; arguments = Fun [FileIn false; String; Int; PyCompilerFlags]; result = Int; optional = false; }; { symbol = "PyRun_StringFlags"; arguments = Fun [String; Input; PyObject false; PyObject false; PyCompilerFlags]; result = PyObject true; optional = false; }; { symbol = "PyRun_SimpleStringFlags"; arguments = Fun [String; PyCompilerFlags]; result = Int; optional = false; }; { symbol = "PySeqIter_New"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyCallIter_New"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PySequence_Check"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PySequence_Concat"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PySequence_Contains"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PySequence_Count"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PySequence_DelItem"; arguments = Fun [PyObject false; Size]; result = Int; optional = false; }; { symbol = "PySequence_DelSlice"; arguments = Fun [PyObject false; Size; Size]; result = Int; optional = false; }; { symbol = "PySequence_Fast"; arguments = Fun [PyObject false; String]; result = PyObject true; optional = false; }; { symbol = "PySequence_GetItem"; arguments = Fun [PyObject false; Size]; result = PyObject true; optional = false; }; { symbol = "PySequence_GetSlice"; arguments = Fun [PyObject false; Size; Size]; result = PyObject true; optional = false; }; { symbol = "PySequence_In"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PySequence_Index"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PySequence_InPlaceConcat"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PySequence_InPlaceRepeat"; arguments = Fun [PyObject false; Size]; result = PyObject true; optional = false; }; { symbol = "PySequence_Length"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PySequence_List"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PySequence_Repeat"; arguments = Fun [PyObject false; Size]; result = PyObject true; optional = false; }; { symbol = "PySequence_SetItem"; arguments = Fun [PyObject false; Size; PyObject false]; result = Int; optional = false; }; { symbol = "PySequence_SetSlice"; arguments = Fun [PyObject false; Size; Size; PyObject false]; result = Int; optional = false; }; { symbol = "PySequence_Size"; arguments = Fun [PyObject false]; result = Size; optional = false; }; { symbol = "PySequence_Tuple"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PySet_New"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PySet_Add"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PySet_Contains"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PySet_Clear"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PySet_Discard"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PySet_Size"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PySet_Type"; arguments = Value; result = PyObject false; optional = false; }; { symbol = "PySlice_New"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyTuple_GetItem"; arguments = Fun [PyObject false; Size]; result = PyObject false; optional = false; }; { symbol = "PyTuple_GetSlice"; arguments = Fun [PyObject false; Size; Size]; result = PyObject true; optional = false; }; { symbol = "PyTuple_New"; arguments = Fun [Size]; result = PyObject true; optional = false; }; { symbol = "PyTuple_SetItem"; arguments = Fun [PyObject false; Size; PyObject true]; result = Int; optional = false; }; { symbol = "PyTuple_Size"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyType_IsSubtype"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyType_Type"; arguments = Value; result = PyObject false; optional = false; };] let wrappers_python2 = [{ symbol = "Py_GetProgramName"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_GetPythonHome"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_GetProgramFullPath"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_GetPrefix"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_GetExecPrefix"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_GetPath"; arguments = Fun []; result = String; optional = false; }; { symbol = "Py_SetProgramName"; arguments = Fun [String]; result = Unit; optional = false; }; { symbol = "Py_SetPythonHome"; arguments = Fun [String]; result = Unit; optional = false; }; { symbol = "Py_CompileStringFlags"; arguments = Fun [String; String; Input; PyCompilerFlags]; result = PyObject true; optional = false; }; { symbol = "PyClass_New"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyExc_StandardError"; arguments = Deref; result = PyObject false; optional = false; }; { symbol = "PyEval_GetRestricted"; arguments = Fun []; result = Int; optional = false; }; { symbol = "PyInstance_New"; arguments = Fun [PyObject false; PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyInstance_NewRaw"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyInt_AsLong"; arguments = Fun [PyObject false]; result = Int64; optional = false; }; { symbol = "PyInt_FromLong"; arguments = Fun [Int64]; result = PyObject true; optional = false; }; { symbol = "PyInt_GetMax"; arguments = Fun []; result = Int64; optional = false; }; { symbol = "PyMethod_Class"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Divide"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_InPlaceDivide"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyNumber_Int"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyObject_Cmp"; arguments = Fun [PyObject false; PyObject false; IntPtr]; result = Int; optional = false; }; { symbol = "PyObject_Compare"; arguments = Fun [PyObject false; PyObject false]; result = Int; optional = false; }; { symbol = "PyObject_Unicode"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyString_AsString"; arguments = Fun [PyObject false]; result = StringOption; optional = false; }; { symbol = "PyString_Format"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyString_FromString"; arguments = Fun [String]; result = PyObject true; optional = false; }; { symbol = "PyString_FromStringAndSize"; arguments = Fun [String; Size]; result = PyObject true; optional = false; }; { symbol = "PyString_Size"; arguments = Fun [PyObject false]; result = Size; optional = false; };] let wrappers_ucs2 = [{ symbol = "PyUnicodeUCS2_AsEncodedString"; arguments = Fun [PyObject false; String; String]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS2_AsUTF8String"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS2_AsUTF16String"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS2_AsUTF32String"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS2_DecodeUTF8"; arguments = Fun [String; Size; StringOption]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS2_DecodeUTF16"; arguments = Fun [String; Size; StringOption; IntPtr]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS2_DecodeUTF32"; arguments = Fun [String; Size; StringOption; IntPtr]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS2_Format"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS2_FromString"; arguments = Fun [String]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS2_GetSize"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyUnicodeUCS2_FromUnicode"; arguments = Fun [UCS2; Size]; result = PyObject false; optional = false; }; { symbol = "PyUnicodeUCS2_AsUnicode"; arguments = Fun [PyObject false]; result = UCS2Option; optional = false; };] let wrappers_ucs4 = [{ symbol = "PyUnicodeUCS4_AsEncodedString"; arguments = Fun [PyObject false; String; String]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS4_AsUTF8String"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS4_AsUTF16String"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS4_AsUTF32String"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS4_DecodeUTF8"; arguments = Fun [String; Size; StringOption]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS4_DecodeUTF16"; arguments = Fun [String; Size; StringOption; IntPtr]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS4_DecodeUTF32"; arguments = Fun [String; Size; StringOption; IntPtr]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS4_Format"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS4_FromString"; arguments = Fun [String]; result = PyObject true; optional = false; }; { symbol = "PyUnicodeUCS4_GetSize"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyUnicodeUCS4_FromUnicode"; arguments = Fun [UCS4; Size]; result = PyObject false; optional = false; }; { symbol = "PyUnicodeUCS4_AsUnicode"; arguments = Fun [PyObject false]; result = UCS4Option false; optional = false; };] let wrappers_python3 = [{ symbol = "Py_GetProgramName"; arguments = Fun []; result = WideString; optional = false; }; { symbol = "Py_GetPythonHome"; arguments = Fun []; result = WideString; optional = false; }; { symbol = "Py_GetProgramFullPath"; arguments = Fun []; result = WideString; optional = false; }; { symbol = "Py_GetPrefix"; arguments = Fun []; result = WideString; optional = false; }; { symbol = "Py_GetExecPrefix"; arguments = Fun []; result = WideString; optional = false; }; { symbol = "Py_GetPath"; arguments = Fun []; result = WideString; optional = false; }; { symbol = "Py_SetProgramName"; arguments = Fun [WideString]; result = Unit; optional = false; }; { symbol = "Py_SetPythonHome"; arguments = Fun [WideString]; result = Unit; optional = false; }; { symbol = "Py_CompileStringExFlags"; arguments = Fun [String; String; Input; PyCompilerFlags; Int]; result = PyObject true; optional = false; }; { symbol = "PyBytes_AsString"; arguments = Fun [PyObject false]; result = StringOption; optional = false; }; { symbol = "PyBytes_FromString"; arguments = Fun [String]; result = PyObject true; optional = false; }; { symbol = "PyBytes_FromStringAndSize"; arguments = Fun [String; Size]; result = PyObject true; optional = false; }; { symbol = "PyBytes_Size"; arguments = Fun [PyObject false]; result = Size; optional = false; }; { symbol = "PyImport_ExecCodeModuleObject"; arguments = Fun [ PyObject false; PyObject false; PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyImport_ExecCodeModuleWithPathnames"; arguments = Fun [String; PyObject false; String; String]; result = PyObject true; optional = false; }; { symbol = "PyImport_ImportModuleLevelObject"; arguments = Fun [ PyObject false; PyObject false; PyObject false; PyObject false; Int]; result = PyObject true; optional = false; }; { symbol = "PyInstanceMethod_New"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_AsEncodedString"; arguments = Fun [PyObject false; String; String]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_AsUTF8String"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_AsUTF16String"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_AsUTF32String"; arguments = Fun [PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_DecodeUTF8"; arguments = Fun [String; Size; StringOption]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_DecodeUTF16"; arguments = Fun [String; Size; StringOption; IntPtr]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_DecodeUTF32"; arguments = Fun [String; Size; StringOption; IntPtr]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_Format"; arguments = Fun [PyObject false; PyObject false]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_FromString"; arguments = Fun [String]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_FromStringAndSize"; arguments = Fun [String; Size]; result = PyObject true; optional = false; }; { symbol = "PyUnicode_GetLength"; arguments = Fun [PyObject false]; result = Size; optional = false; }; { symbol = "PyUnicode_GetSize"; arguments = Fun [PyObject false]; result = Int; optional = false; }; { symbol = "PyUnicode_FromKindAndData"; arguments = Fun [Int; UCS4; Size]; result = PyObject false; optional = false; }; { symbol = "PyUnicode_AsUCS4Copy"; arguments = Fun [PyObject false]; result = UCS4Option true; optional = false; };] let string_of_type_ml ty = match ty with PyObject _ -> "Pytypes.pyobject" | PyCompilerFlags -> "int ref option" | String | WideString -> "string" | Int | Long | Size -> "int" | FileIn _ | FileOut _ -> "Unix.file_descr Pytypes.file" | Int64 -> "int64" | IntPtr -> "int ref" | Compare -> "Pytypes.compare" | Unit -> "unit" | Input -> "Pytypes.input" | Double -> "float" | StringOption -> "string option" | NeverReturn -> "'a" | UCS2 | UCS4 -> "int array" | UCS2Option | UCS4Option _ -> "int array option" let decapitalize prefix symbol = prefix ^ symbol let wrapper_name prefix symbol = Printf.sprintf "%s%s_wrapper" prefix symbol let bytecode_name prefix symbol = Printf.sprintf "%s%s_bytecode" prefix symbol let native_name prefix symbol = Printf.sprintf "%s%s_native" prefix symbol let print_external indent prefix channel wrapper = let symbol = wrapper.symbol in let symbol_lowercase = String.lowercase_ascii symbol in let arguments = wrapper.arguments in let ty_arguments = match arguments with Value | Deref | Fun [] -> "unit" | Fun arguments' -> String.concat " -> " (List.map string_of_type_ml arguments') in let ty_result = string_of_type_ml wrapper.result in let ty = ty_arguments ^ " -> " ^ ty_result in let decl_start = Printf.sprintf "%sexternal %s:" indent symbol_lowercase in let decl_middle = Printf.sprintf " %s" ty in let decl_end = match arguments with Fun arguments' when List.length arguments' > 5 -> Printf.sprintf " = \"%s\" \"%s\"" (bytecode_name prefix symbol) (native_name prefix symbol) | _ -> Printf.sprintf " = \"%s\"" (wrapper_name prefix symbol) in if String.length decl_start + String.length decl_middle > 80 then Printf.fprintf channel "%s\n %s\n %s\n" decl_start decl_middle decl_end else if String.length decl_start + String.length decl_middle + String.length decl_end > 80 then Printf.fprintf channel "%s%s\n %s\n" decl_start decl_middle decl_end else Printf.fprintf channel "%s%s%s\n" decl_start decl_middle decl_end let print_externals indent prefix channel wrappers = List.iter (print_external indent prefix channel) wrappers let print_pycaml indent prefix channel wrapper = let symbol = wrapper.symbol in let symbol_lowercase = String.lowercase_ascii symbol in let arguments = wrapper.arguments in let arguments_list = match arguments with Value | Deref -> [] | Fun list -> List.mapi (fun i _ -> Printf.sprintf "arg%d" i) list in let arguments_tuple = match arguments with Value | Deref -> "" | Fun [] -> " ()" | Fun [_] -> " arg" | Fun _list -> Printf.sprintf " (%s)" (String.concat ", " arguments_list) in let convert i ty = let arg = Printf.sprintf "arg%d" i in match ty with Compare -> Printf.sprintf "(Pytypes.compare_of_int %s)" arg | Input -> Printf.sprintf "(Pytypes.input_of_int %s)" arg | FileIn _ | FileOut _ -> Printf.sprintf "(Pytypes.Channel (Pyml_arch.fd_of_int %s))" arg | _ -> arg in let converted_arguments_list = match arguments with Value | Deref -> [] | Fun list -> List.mapi convert list in let arguments_curryfied = match arguments with Value | Deref -> "" | Fun [] -> " ()" | Fun [_] -> " arg" | Fun _list -> Printf.sprintf " %s" (String.concat " " converted_arguments_list) in Printf.fprintf channel "%slet %s%s = %s%s%s\n" indent symbol_lowercase arguments_tuple prefix symbol_lowercase arguments_curryfied let print_pycamls indent prefix channel wrappers = List.iter (print_pycaml indent prefix channel) wrappers module Set_string = Set.Make(String) let print_all_externals channel = Printf.fprintf channel "(** Low-level bindings. *) (** The library has to be initialized via {!Py.initialize} first. *) "; print_externals "" "Python_" channel wrappers; Printf.fprintf channel " (** Python 2 specific bindings. *) module Python2 = struct\n"; print_externals " " "Python2_" channel wrappers_python2; Printf.fprintf channel "end\n (** UCS2 specific bindings. *) module UCS2 = struct\n"; print_externals " " "UCS2_" channel wrappers_ucs2; Printf.fprintf channel "end\n (** UCS4 specific bindings. *) module UCS4 = struct\n"; print_externals " " "UCS4_" channel wrappers_ucs4; Printf.fprintf channel "end\n (** Python 3 specific bindings. *) module Python3 = struct\n"; print_externals " " "Python3_" channel wrappers_python3; Printf.fprintf channel "end\n (** Automatic wrappers for Pycaml_compat. *) module Pycaml = struct\n"; print_pycamls " " "" channel wrappers; let wrappers_python2_not_in_python3 = let python3_symbols = List.map (fun w -> w.symbol) wrappers_python3 |> Set_string.of_list in List.filter (fun w -> not (Set_string.mem w.symbol python3_symbols)) wrappers_python2 in print_pycamls " " "Python2." channel wrappers_python2_not_in_python3; print_pycamls " " "Python3." channel wrappers_python3; Printf.fprintf channel "end\n" let print_dlsym indent prefix channel wrapper = let symbol = wrapper.symbol in let symbol_decapitalized = decapitalize prefix symbol in let resolve = if wrapper.optional then "resolve_optional" else "resolve" in if wrapper.arguments = Deref then Printf.fprintf channel "%s%s = deref_not_null(%s(\"%s\"));\n" indent symbol_decapitalized resolve symbol else if wrapper.arguments = Value then Printf.fprintf channel "%s%s = %s(\"%s\");\n" indent symbol_decapitalized resolve symbol else Printf.fprintf channel "%s%s = %s(\"%s\");\n" indent symbol_decapitalized resolve symbol let print_dlsyms indent prefix channel wrappers = List.iter (print_dlsym indent prefix channel) wrappers let print_all_dlsyms channel = print_dlsyms "" "Python_" channel wrappers; Printf.fprintf channel "if (version_major <= 2) {"; print_dlsyms " " "Python2_" channel wrappers_python2; Printf.fprintf channel "}\nelse {\n"; print_dlsyms " " "Python3_" channel wrappers_python3; Printf.fprintf channel "} switch (ucs) { case UCS2: "; print_dlsyms " " "UCS2_" channel wrappers_ucs2; Printf.fprintf channel "break; case UCS4: "; print_dlsyms " " "UCS4_" channel wrappers_ucs4; Printf.fprintf channel "break; case UCS_NONE: break; }\n" let string_of_type_c ty = match ty with PyObject _ -> "PyObject *" | PyCompilerFlags -> "PyCompilerFlags *" | String | StringOption -> "const char *" | WideString -> "wchar_t *" | Int -> "int" | Long | Int64 -> "long" | Size -> "Py_ssize_t" | IntPtr -> "int *" | Compare -> "int" | Unit | NeverReturn -> "void" | Input -> "int" | FileIn _ | FileOut _ -> "FILE *" | Double -> "double" | UCS2 | UCS2Option -> "int16_t *" | UCS4 | UCS4Option _ -> "int32_t *" let print_declaration prefix channel wrapper = let symbol = wrapper.symbol in let symbol_decapitalized = decapitalize prefix symbol in let arguments = wrapper.arguments in let result = wrapper.result in let ty_result = string_of_type_c result in let ty_result = if String.sub ty_result (String.length ty_result - 1) 1 = "*" then ty_result else ty_result ^ " " in match arguments with Value | Deref -> Printf.fprintf channel "static %s %s;\n" ty_result symbol_decapitalized | Fun arguments' -> let ty_arguments = if arguments' = [] then "void" else String.concat ", " (List.map string_of_type_c arguments') in Printf.fprintf channel "static %s(*%s)(%s);\n" ty_result symbol_decapitalized ty_arguments let print_declarations prefix channel wrappers = List.iter (print_declaration prefix channel) wrappers let print_all_declarations channel = print_declarations "Python_" channel wrappers; Printf.fprintf channel "\n/* Python 2 */\n"; print_declarations "Python2_" channel wrappers_python2; Printf.fprintf channel "\n/* UCS 2 */\n"; print_declarations "UCS2_" channel wrappers_ucs2; Printf.fprintf channel "\n/* UCS 4 */\n"; print_declarations "UCS4_" channel wrappers_ucs4; Printf.fprintf channel "\n/* Python 3 */\n"; print_declarations "Python3_" channel wrappers_python3 let coercion_of_caml ty v = match ty with PyObject _ -> Printf.sprintf "pyml_unwrap(%s)" v | String -> Printf.sprintf "String_val(%s)" v | WideString -> Printf.sprintf "pyml_unwrap_wide_string(%s)" v | Int | Long | Size -> Printf.sprintf "Int_val(%s)" v | Int64 -> Printf.sprintf "Int64_val(%s)" v | IntPtr -> Printf.sprintf "pyml_unwrap_intref(%s)" v | PyCompilerFlags -> Printf.sprintf "pyml_unwrap_compilerflags(%s)" v | Compare -> Printf.sprintf "Int_val(%s)" v | Unit | NeverReturn | UCS2Option | UCS4Option _ -> assert false | Input -> Printf.sprintf "256 + Int_val(%s)" v | FileIn _ -> Printf.sprintf "open_file(%s, \"r\")" v | FileOut _ -> Printf.sprintf "open_file(%s, \"w\")" v | Double -> Printf.sprintf "Double_val(%s)" v | StringOption -> Printf.sprintf "Is_block(%s) ? String_val(Field(%s, 0)) : NULL" v v | UCS2 -> Printf.sprintf "pyml_unwrap_ucs2(%s)" v | UCS4 -> Printf.sprintf "pyml_unwrap_ucs4(%s)" v let string_of_bool b = if b then "true" else "false" let coercion_of_c ty = match ty with PyObject b -> Printf.sprintf " CAMLreturn(pyml_wrap(result, %s));" (string_of_bool b) | String -> Printf.sprintf " CAMLreturn(caml_copy_string(result));" | StringOption -> Printf.sprintf " CAMLreturn(pyml_wrap_string_option(result));" | WideString -> Printf.sprintf " CAMLreturn(pyml_wrap_wide_string(result));" | Int | Long | Size | Compare -> Printf.sprintf " CAMLreturn(Val_int(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));" | Unit | NeverReturn -> Printf.sprintf " CAMLreturn(Val_unit);" | Input | FileIn _ | FileOut _ | UCS2 | UCS4 -> assert false | Double -> Printf.sprintf " CAMLreturn(caml_copy_double(result));" | UCS2Option -> Printf.sprintf " CAMLreturn(pyml_wrap_ucs2_option(result));" | UCS4Option free -> Printf.sprintf " CAMLreturn(pyml_wrap_ucs4_option_and_free(result, %s));" (string_of_bool free) let space_if_not_starred s = if s.[String.length s - 1] = '*' then s else s ^ " " let string_of_type_c_argument ty = match ty with IntPtr -> "int" | _ -> string_of_type_c ty let rec seq a b = if a < b then a :: seq (succ a) b else [] let print_stub prefix pyml_assert_initialized channel wrapper = let symbol = wrapper.symbol in let arguments = wrapper.arguments in let need_bytecode = match wrapper.arguments with Value | Deref -> false | Fun arguments' -> List.length arguments' > 5 in let symbol_wrapper = if need_bytecode then native_name prefix symbol else wrapper_name prefix symbol in let symbol_decapitalized = decapitalize prefix symbol in let result = wrapper.result in let stub_arguments = match arguments with Value | Deref | Fun [] -> "value unit" | Fun arguments' -> let value_arg i _ = Printf.sprintf "value arg%d_ocaml" i in let stub_argument_list = List.mapi value_arg arguments' in String.concat ", " stub_argument_list in let arg_ocaml i = Printf.sprintf "arg%d_ocaml" i in let camlparam = match arguments with Value | Deref | Fun [] -> " CAMLparam1(unit);" | Fun arguments' -> let result = Buffer.create 17 in let count = List.length arguments' in let camlparam s a b = Buffer.add_string result (Printf.sprintf " %s%d(%s);\n" s (b - a) (String.concat ", " (List.map arg_ocaml (seq a b)))) in camlparam "CAMLparam" 0 (min count 5); for i = 1 to (count - 1) / 5 do camlparam "CAMLxparam" (i * 5) (min ((i + 1) * 5) count) done; Buffer.contents result in let pyml_assert_initialized = if wrapper.optional then pyml_assert_initialized ^ Printf.sprintf " pyml_check_symbol_available(%s, \"%s\");" symbol_decapitalized symbol else pyml_assert_initialized in let destruct_argument i ty = let ty_c = string_of_type_c_argument ty in let coercion = coercion_of_caml ty (arg_ocaml i) in let inc = if ty = PyObject true then Printf.sprintf "\n Py_INCREF(arg%d);" i else "" in Printf.sprintf " %sarg%d = %s;%s" (space_if_not_starred ty_c) i coercion inc in let destruct_arguments = let destruct_argument_list = match arguments with Value | Deref -> [] | Fun arguments' -> List.mapi destruct_argument arguments' in String.concat "\n" destruct_argument_list in let result_type_c = string_of_type_c result in let make_arg i ty = match ty with IntPtr -> Printf.sprintf "&arg%d" i | _ -> Printf.sprintf "arg%d" i in let callarg = match arguments with Value | Deref -> symbol_decapitalized | Fun arguments' -> let arg_c_list = List.mapi make_arg arguments' in let arg_c = String.concat ", " arg_c_list in Printf.sprintf "%s(%s)" symbol_decapitalized arg_c in let call = match result with Unit | NeverReturn -> Printf.sprintf " %s;" callarg | _ -> Printf.sprintf " %sresult = %s;" (space_if_not_starred result_type_c) callarg in let free_argument i ty = match ty with PyCompilerFlags | UCS2 | UCS4 -> Printf.sprintf "\n free(arg%d);" i | FileIn true | FileOut true -> Printf.sprintf "\n close_file(arg%d_ocaml, arg%d);" i i | _ -> "" in let free = match arguments with Value | Deref -> "" | Fun arguments' -> String.concat "" (List.mapi free_argument arguments') in let return = coercion_of_c result in Printf.fprintf channel " CAMLprim value %s(%s) { %s %s %s %s%s %s } " symbol_wrapper stub_arguments camlparam pyml_assert_initialized destruct_arguments call free return; if need_bytecode then let arguments' = match arguments with Value | Deref -> assert false | Fun arguments' -> arguments' in Printf.fprintf channel " CAMLprim value %s(value *argv, int argn) { return %s(%s); } " (bytecode_name prefix symbol) (native_name prefix symbol) (String.concat ", " (List.mapi (fun i _ -> Printf.sprintf "argv[%d]" i) arguments')) let print_stubs prefix pyml_assert_initialized channel wrappers = List.iter (print_stub prefix pyml_assert_initialized channel) wrappers let print_all_stubs channel = print_stubs "Python_" "pyml_assert_initialized();" channel wrappers; Printf.fprintf channel "\n/* Python 2 */\n"; print_stubs "Python2_" "pyml_assert_python2();" channel wrappers_python2; Printf.fprintf channel "\n/* UCS 2 */\n"; print_stubs "UCS2_" "pyml_assert_ucs2();" channel wrappers_ucs2; Printf.fprintf channel "\n/* UCS 4 */\n"; print_stubs "UCS4_" "pyml_assert_ucs4();" channel wrappers_ucs4; Printf.fprintf channel "\n/* Python 3 */\n"; print_stubs "Python3_" "pyml_assert_python3();" channel wrappers_python3 let write_file filename f = let channel = open_out filename in try let result = f channel in close_out channel; result with e -> close_out_noerr channel; raise e let () = write_file "pywrappers.ml" print_all_externals; write_file "pyml_dlsyms.inc" print_all_dlsyms; write_file "pyml.h" print_all_declarations; write_file "pyml_wrappers.inc" print_all_stubs numpy.ml000066400000000000000000000075571452051003100125650ustar00rootroot00000000000000external pyarray_of_bigarray: Py.Object.t -> Py.Object.t -> ('a, 'b, 'c) Bigarray.Genarray.t -> Py.Object.t = "pyarray_of_bigarray_wrapper" external bigarray_of_pyarray: Py.Object.t -> Py.Object.t -> ('a, 'b) Bigarray.kind * 'c Bigarray.layout * ('a, 'b, 'c) Bigarray.Genarray.t = "bigarray_of_pyarray_wrapper" let pyarray_subtype_ref = ref None let () = Py.on_finalize (fun () -> pyarray_subtype_ref := None) let pyarray_subtype () = match !pyarray_subtype_ref with Some pyarray_subtype -> pyarray_subtype | None -> let pyarray_type = Py.Array.pyarray_type () in let pyarray_subtype = Py.Type.create "ocamlbigarray" [pyarray_type] [("ocamlbigarray", Py.none)] in pyarray_subtype_ref := Some pyarray_subtype; pyarray_subtype let of_bigarray bigarray = let result = pyarray_of_bigarray (Py.Array.numpy_api ()) (pyarray_subtype ()) bigarray in let result = Py.check_not_null result in let capsule = Py.Capsule.unsafe_wrap_value bigarray in Py.Object.set_attr_string result "ocamlbigarray" capsule; result (* written with equalities to support OCaml pre-GADT *) let string_of_kind kind = if kind = Obj.magic Bigarray.float32 then "float32/NPY_FLOAT" else if kind = Obj.magic Bigarray.float64 then "float64/NPY_DOUBLE" else if kind = Obj.magic Bigarray.int8_signed then "int8_signed/NPY_BYTE" else if kind = Obj.magic Bigarray.int8_unsigned then "int8_unsigned/NPY_UBYTE" else if kind = Obj.magic Bigarray.int16_signed then "int16_signed/NPY_SHORT" else if kind = Obj.magic Bigarray.int16_unsigned then "int16_unsigned/NPY_USHORT" else if kind = Obj.magic Bigarray.int32 then "int32/NPY_INT" else if kind = Obj.magic Bigarray.int64 then "int64/NPY_LONGLONG" else if kind = Obj.magic Bigarray.int then "int" else if kind = Obj.magic Bigarray.nativeint then "nativeint/NPY_LONG" else if kind = Obj.magic Bigarray.complex32 then "complex32/NPY_CFLOAT" else if kind = Obj.magic Bigarray.complex64 then "complex64/NPY_CDOUBLE" else if kind = Obj.magic Bigarray.char then "char/NPY_CHAR" else "unknown kind" let string_of_layout layout = if layout = Obj.magic Bigarray.c_layout then "C" else if layout = Obj.magic Bigarray.fortran_layout then "Fortran" else "unknown layout" let to_bigarray kind layout t = if not (Py.Object.is_instance t (Py.Array.pyarray_type ())) then invalid_arg "Numpy.to_bigarray"; let kind', layout', array = bigarray_of_pyarray (Py.Array.numpy_api ()) t in if kind <> kind' then invalid_arg (Printf.sprintf "Numpy.to_bigarray: Numpy array has elements of kind %s, but to_bigarray expected %s" (string_of_kind kind') (string_of_kind kind)); if layout <> layout' then invalid_arg (Printf.sprintf "Numpy.to_bigarray: Numpy array has %s layout, but to_bigarray expected %s" (string_of_layout layout') (string_of_layout layout)); array type ('a, 'b, 'c) to_bigarray = { kind : ('a, 'b) Bigarray.kind ; layout : 'c Bigarray.layout ; array : ('a, 'b, 'c) Bigarray.Genarray.t } type 'r to_bigarray_k = { f : 'a 'b 'c . ('a, 'b, 'c) to_bigarray -> 'r } let to_bigarray_k (k : 'r to_bigarray_k) t : 'r = if not (Py.Object.is_instance t (Py.Array.pyarray_type ())) then invalid_arg "Numpy.to_bigarray"; let kind, layout, array = bigarray_of_pyarray (Py.Array.numpy_api ()) t in k.f { kind; layout; array } external compare_kind : ('a, 'b) Bigarray.kind -> ('c, 'd) Bigarray.kind -> int = "%compare" external compare_layout : 'a Bigarray.layout -> 'b Bigarray.layout -> int = "%compare" let check_kind_and_layout (kind : ('a, 'b) Bigarray.kind) (layout : 'c Bigarray.layout) t : ('a, 'b, 'c) Bigarray.Genarray.t option = if compare_kind kind (Bigarray.Genarray.kind t) = 0 && compare_layout layout (Bigarray.Genarray.layout t) = 0 then Some (Obj.magic t) else None numpy.mli000066400000000000000000000055551452051003100127320ustar00rootroot00000000000000(** OCaml Interface for Numpy. *) (** Arrays are passed in place (without copy): Python and OCaml programs can change the contents of the array and the changes are visible in the other language. *) (** The following table gives the correspondence between bigarray kinds and Numpy element types. {ul {li [float32] / [NPY_FLOAT]} {li [float64] / [NPY_DOUBLE]} {li [int8_signed] / [NPY_BYTE]} {li [int8_unsigned] / [NPY_UBYTE]} {li [int16_signed] / [NPY_SHORT]} {li [int16_unsigned] / [NPY_USHORT]} {li [int32] / [NPY_INT]} {li [int64] / [NPY_LONGLONG]} {li [nativeint] / [NPY_LONG]} {li [complex32] / [NPY_CFLOAT]} {li [complex64] / [NPY_CDOUBLE]} {li [char] / [NPY_CHAR]}} Other kinds/element types are not supported. In particular, OCaml integer kind, [int], has no equivalent type in Numpy. *) val of_bigarray: ('a, 'b, 'c) Bigarray.Genarray.t -> Py.Object.t (** [of_bigarray a] returns a Numpy array that shares the same contents than the OCaml array [a]. *) val to_bigarray: ('a, 'b) Bigarray.kind -> 'c Bigarray.layout -> Py.Object.t -> ('a, 'b, 'c) Bigarray.Genarray.t (** [to_bigarray kind layout a] returns a bigarray that shares the same contents than the Numpy array [a]. If `kind` and/or `layout` are unknown, you may use {!val:to_bigarray_k}. *) type ('a, 'b, 'c) to_bigarray = { kind : ('a, 'b) Bigarray.kind ; layout : 'c Bigarray.layout ; array : ('a, 'b, 'c) Bigarray.Genarray.t } type 'r to_bigarray_k = { f : 'a 'b 'c . ('a, 'b, 'c) to_bigarray -> 'r } val to_bigarray_k : 'r to_bigarray_k -> Py.Object.t -> 'r (** [to_bigarray_k k a] calls [k.f] with the contents of the Numpy array [a]. [k.f] has to be polymorphic in the kind and the layout of the bigarray: functions {!val:compare_kind}, {!val:compare_layout} and {!val:check_kind_and_layout} can be used to introspect the bigarray polymorphically. *) val compare_kind : ('a, 'b) Bigarray.kind -> ('c, 'd) Bigarray.kind -> int (** [compare_kind] provides a total order on {!val:Bigarray.kind}. As opposed to generic [compare] of OCaml standard libary, [compare_kind] is polymorphic in the kind of the bigarray. *) val compare_layout : 'a Bigarray.layout -> 'b Bigarray.layout -> int (** [compare_layout] provides a total order on {!val:Bigarray.layout}. As opposed to generic [compare] of OCaml standard libary, [compare_kind] is polymorphic in the layout of the bigarray. *) val check_kind_and_layout : ('a, 'b) Bigarray.kind -> 'c Bigarray.layout -> ('d, 'e, 'f) Bigarray.Genarray.t -> ('a, 'b, 'c) Bigarray.Genarray.t option (** [check_kind_and_layout kind layout a] returns [Some a] if [a] has the given [kind] and [layout] (that is to say, if we have the following type equalities, ['a = 'd], ['b = 'e] and ['c = 'f]). This function allows the callback of {!val:to_bigarray_k} to be polymorphic in the kind of the array. *) numpy_stubs.c000066400000000000000000000135321452051003100136050ustar00rootroot00000000000000#include #include #include #include #include #include #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 = (struct numpy_custom_operations *) Custom_ops_val(v); Py_DECREF(ops->obj); free(ops); } CAMLprim value pyarray_of_bigarray_wrapper( value numpy_api_ocaml, value bigarray_type_ocaml, value bigarray_ocaml) { CAMLparam3(numpy_api_ocaml, bigarray_type_ocaml, bigarray_ocaml); pyml_assert_initialized(); PyObject *c_api = pyml_unwrap(numpy_api_ocaml); void **PyArray_API = pyml_get_pyarray_api(c_api); PyObject *(*PyArray_New) (PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *) = PyArray_API[93]; int nd = Caml_ba_array_val(bigarray_ocaml)->num_dims; npy_intp *dims = malloc(nd * sizeof(npy_intp)); int i; for (i = 0; i < nd; i++) { dims[i] = Caml_ba_array_val(bigarray_ocaml)->dim[i]; } int type_num; intnat flags = Caml_ba_array_val(bigarray_ocaml)->flags; switch (flags & CAML_BA_KIND_MASK) { case CAML_BA_FLOAT32: type_num = NPY_FLOAT; break; case CAML_BA_FLOAT64: type_num = NPY_DOUBLE; break; case CAML_BA_SINT8: type_num = NPY_BYTE; break; case CAML_BA_UINT8: type_num = NPY_UBYTE; break; case CAML_BA_SINT16: type_num = NPY_SHORT; break; case CAML_BA_UINT16: type_num = NPY_USHORT; break; case CAML_BA_INT32: type_num = NPY_INT; break; case CAML_BA_INT64: type_num = NPY_LONGLONG; break; case CAML_BA_CAML_INT: caml_failwith("Caml integers are unsupported for NumPy array"); break; case CAML_BA_NATIVE_INT: type_num = NPY_LONG; break; case CAML_BA_COMPLEX32: type_num = NPY_CFLOAT; break; case CAML_BA_COMPLEX64: type_num = NPY_CDOUBLE; break; #ifdef CAML_BA_CHAR /* introduced in 4.02.0 */ case CAML_BA_CHAR: type_num = NPY_CHAR; break; #endif default: caml_failwith("Unsupported bigarray kind for NumPy array"); } int np_flags; switch (flags & CAML_BA_LAYOUT_MASK) { case CAML_BA_C_LAYOUT: np_flags = NPY_ARRAY_CARRAY; break; case CAML_BA_FORTRAN_LAYOUT: np_flags = NPY_ARRAY_FARRAY; break; default: caml_failwith("Unsupported bigarray layout for NumPy array"); } void *data = Caml_ba_data_val(bigarray_ocaml); PyTypeObject (*PyArray_SubType) = (PyTypeObject *) pyml_unwrap(bigarray_type_ocaml); PyObject *result = PyArray_New( PyArray_SubType, nd, dims, type_num, NULL, data, 0, np_flags, NULL); free(dims); CAMLreturn(pyml_wrap(result, true)); } CAMLprim value bigarray_of_pyarray_wrapper( value numpy_api_ocaml, value pyarray_ocaml) { CAMLparam2(numpy_api_ocaml, pyarray_ocaml); CAMLlocal2(bigarray, result); pyml_assert_initialized(); PyObject *array = pyml_unwrap(pyarray_ocaml); PyArrayObject_fields *fields = (PyArrayObject_fields *) pyobjectdescr(array); int nd = fields->nd; npy_intp *shape = fields->dimensions; intnat *dims = malloc(nd * sizeof(intnat)); int i; for (i = 0; i < nd; i++) { dims[i] = shape[i]; } int type = fields->descr->type_num; enum caml_ba_kind kind; switch (type) { case NPY_BYTE: kind = CAML_BA_SINT8; break; case NPY_UBYTE: kind = CAML_BA_UINT8; break; case NPY_SHORT: kind = CAML_BA_SINT16; break; case NPY_USHORT: kind = CAML_BA_UINT16; break; case NPY_INT: kind = CAML_BA_INT32; break; case NPY_LONG: kind = CAML_BA_NATIVE_INT; break; case NPY_LONGLONG: kind = CAML_BA_INT64; break; case NPY_FLOAT: kind = CAML_BA_FLOAT32; break; case NPY_DOUBLE: kind = CAML_BA_FLOAT64; break; case NPY_CFLOAT: kind = CAML_BA_COMPLEX32; break; case NPY_CDOUBLE: kind = CAML_BA_COMPLEX64; break; case NPY_CHAR: #ifdef CAML_BA_CHAR /* introduced in 4.02.0 */ kind = CAML_BA_CHAR; #else kind = CAML_BA_UINT8; #endif break; default: caml_failwith("Unsupported NumPy kind for bigarray"); } int flags = fields->flags; enum caml_ba_layout layout; if (flags & NPY_ARRAY_C_CONTIGUOUS) { layout = CAML_BA_C_LAYOUT; } else if (flags & NPY_ARRAY_F_CONTIGUOUS) { layout = CAML_BA_FORTRAN_LAYOUT; } else { caml_failwith("Unsupported NumPy layout for bigarray"); } void *data = fields->data; bigarray = caml_ba_alloc(kind | layout, nd, data, dims); free(dims); Py_INCREF(array); 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; newops->ops.finalize = numpy_finalize; newops->ops.compare = oldops->compare; newops->ops.hash = oldops->hash; newops->ops.serialize = oldops->serialize; newops->ops.deserialize = oldops->deserialize; newops->ops.compare_ext = oldops->compare_ext; newops->obj = array; Custom_ops_val(bigarray) = (struct custom_operations *) newops; result = caml_alloc_tuple(3); Store_field(result, 0, Val_int(kind)); Store_field(result, 1, Val_int(layout == CAML_BA_FORTRAN_LAYOUT ? 1 : 0)); Store_field(result, 2, bigarray); CAMLreturn(result); } numpy_tests.ml000066400000000000000000000155771452051003100140100ustar00rootroot00000000000000let () = Pyml_tests_common.add_test ~title:"of_bigarray" (fun () -> if Py.Import.try_import_module "numpy" = None then Pyml_tests_common.Disabled "numpy is not available" else begin let array = [| 1.; 2. |] in let array1 = Bigarray.Array1.of_array (Bigarray.float64) (Bigarray.c_layout) array in let bigarray = Bigarray.genarray_of_array1 array1 in let a = Numpy.of_bigarray bigarray in let m = Py.Import.add_module "test" in Py.Module.set m "array" a; assert (Py.Run.simple_string " from test import array assert len(array) == 2 assert array[0] == 1. assert array[1] == 2. array[0] = 42. array[1] = 43. "); assert (Bigarray.Array1.get array1 0 = 42.); assert (Bigarray.Array1.get array1 1 = 43.); Pyml_tests_common.Passed end) let () = Pyml_tests_common.add_test ~title:"of_bigarray2" (fun () -> if Py.Import.try_import_module "numpy" = None then Pyml_tests_common.Disabled "numpy is not available" else begin let array = [| [| 1.; 2.; 3. |]; [| -1.23; Stdcompat.Float.nan; 2.72 |] |] in let array2 = Bigarray.Array2.of_array (Bigarray.float64) (Bigarray.c_layout) array in let bigarray = Bigarray.genarray_of_array2 array2 in let a = Numpy.of_bigarray bigarray in let m = Py.Import.add_module "test" in Py.Module.set m "array" a; assert (Py.Run.simple_string " from test import array import numpy assert list(array.shape) == [2, 3] numpy.testing.assert_almost_equal(array[0], [1, 2, 3]) assert(numpy.isnan(array[1, 1])) array[0, 0] = 42. array[0, 1] = 43. array[1, 1] = 1. "); assert (Bigarray.Array2.get array2 0 0 = 42.); assert (Bigarray.Array2.get array2 0 1 = 43.); assert (Bigarray.Array2.get array2 1 1 = 1.); Pyml_tests_common.Passed end) let () = Pyml_tests_common.add_test ~title:"to_bigarray" (fun () -> if Py.Import.try_import_module "numpy" = None then Pyml_tests_common.Disabled "numpy is not available" else begin let m = Py.Import.add_module "test" in let callback arg = let bigarray = Numpy.to_bigarray Bigarray.nativeint Bigarray.c_layout arg.(0) in assert (Bigarray.Genarray.dims bigarray = [| 4 |]); let array1 = Bigarray.array1_of_genarray bigarray in assert (Bigarray.Array1.get array1 0 = 0n); assert (Bigarray.Array1.get array1 1 = 1n); assert (Bigarray.Array1.get array1 2 = 2n); assert (Bigarray.Array1.get array1 3 = 3n); Py.none in Py.Module.set m "callback" (Py.Callable.of_function callback); assert (Py.Run.simple_string " from test import callback import numpy callback(numpy.array([0,1,2,3])) "); Pyml_tests_common.Passed end) let assert_almost_eq ?(eps = 1e-7) f1 f2 = if Stdcompat.Float.abs (f1 -. f2) > eps then failwith (Printf.sprintf "%f <> %f" f1 f2) let () = Pyml_tests_common.add_test ~title:"to_bigarray2" (fun () -> if Py.Import.try_import_module "numpy" = None then Pyml_tests_common.Disabled "numpy is not available" else begin let m = Py.Import.add_module "test" in let callback arg = let bigarray = Numpy.to_bigarray Bigarray.float32 Bigarray.c_layout arg.(0) in assert (Bigarray.Genarray.dims bigarray = [| 2; 4 |]); let array2 = Bigarray.array2_of_genarray bigarray in let assert_almost_eq i j v = assert_almost_eq (Bigarray.Array2.get array2 i j) v in let assert_is_nan i j = let v = Bigarray.Array2.get array2 i j in assert (Stdcompat.Float.is_nan v) in assert_almost_eq 0 0 0.12; assert_almost_eq 0 1 1.23; assert_almost_eq 0 2 2.34; assert_almost_eq 0 3 3.45; assert_almost_eq 1 0 (-1.); assert_is_nan 1 1; assert_almost_eq 1 2 1.; assert_almost_eq 1 3 0.; Py.none in Py.Module.set m "callback" (Py.Callable.of_function callback); assert (Py.Run.simple_string " from test import callback import numpy callback(numpy.array([[0.12,1.23,2.34,3.45],[-1.,numpy.nan,1.,0.]], dtype=numpy.float32)) "); Pyml_tests_common.Passed end) let assert_invalid_argument f = try let () = f () in assert false with Invalid_argument _ -> () let () = Pyml_tests_common.add_test ~title:"to_bigarray invalid type" (fun () -> if Py.Import.try_import_module "numpy" = None then Pyml_tests_common.Disabled "numpy is not available" else begin assert_invalid_argument (fun () -> ignore (Numpy.to_bigarray Float64 C_layout Py.none)); assert_invalid_argument (fun () -> ignore (Numpy.to_bigarray Float64 C_layout (Py.Int.of_int 0))); let array = Numpy.of_bigarray (Bigarray.genarray_of_array1 ( Bigarray.Array1.of_array (Bigarray.float64) (Bigarray.c_layout) [| 1.; 2. |])) in ignore (Numpy.to_bigarray Float64 C_layout array); assert_invalid_argument (fun () -> ignore (Numpy.to_bigarray Float32 C_layout array)); assert_invalid_argument (fun () -> ignore (Numpy.to_bigarray Float64 Fortran_layout array)); Pyml_tests_common.Passed end) let () = Pyml_tests_common.add_test ~title:"to_bigarray_k" (fun () -> if Py.Import.try_import_module "numpy" = None then Pyml_tests_common.Disabled "numpy is not available" else begin let m = Py.Import.add_module "test" in let callback arg = let k { Numpy.kind; layout; array } = assert (Numpy.compare_kind kind Bigarray.nativeint = 0); assert (Numpy.compare_layout layout Bigarray.c_layout = 0); let bigarray = Stdcompat.Option.get (Numpy.check_kind_and_layout Bigarray.nativeint Bigarray.c_layout array) in assert (Bigarray.Genarray.dims bigarray = [| 4 |]); let array1 = Bigarray.array1_of_genarray bigarray in assert (Bigarray.Array1.get array1 0 = 0n); assert (Bigarray.Array1.get array1 1 = 1n); assert (Bigarray.Array1.get array1 2 = 2n); assert (Bigarray.Array1.get array1 3 = 3n) in Numpy.to_bigarray_k { Numpy.f = k } arg.(0); Py.none in Py.Module.set m "callback" (Py.Callable.of_function callback); assert (Py.Run.simple_string " from test import callback import numpy callback(numpy.array([0,1,2,3])) "); Pyml_tests_common.Passed end) let () = if not !Sys.interactive then Pyml_tests_common.main () py.ml000066400000000000000000002574711452051003100120470ustar00rootroot00000000000000module Stdlib_printexc = Printexc (* Workaround for opaque Printexc bug in Stdcompat 9 *) open Stdcompat 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 type ucs = UCSNone | UCS2 | UCS4 type closure = WithoutKeywords of (pyobject -> pyobject) | WithKeywords of (pyobject -> pyobject -> pyobject) external load_library: string option -> bool option -> unit = "py_load_library" external is_debug_build: unit -> bool = "py_is_debug_build" external unsetenv: string -> unit = "py_unsetenv" external finalize_library: unit -> unit = "py_finalize_library" external pywrap_closure: string option -> string -> closure -> pyobject = "pyml_wrap_closure" external pynull: unit -> pyobject = "PyNull_wrapper" external pynone: unit -> pyobject = "PyNone_wrapper" external pytrue: unit -> pyobject = "PyTrue_wrapper" external pyfalse: unit -> pyobject = "PyFalse_wrapper" external pytuple_empty: unit -> pyobject = "PyTuple_Empty_wrapper" external pyobject_callfunctionobjargs: pyobject -> pyobject array -> pyobject = "PyObject_CallFunctionObjArgs_wrapper" external pyobject_callmethodobjargs: pyobject -> pyobject -> pyobject array -> pyobject = "PyObject_CallMethodObjArgs_wrapper" external pyerr_fetch_internal: unit -> pyobject * pyobject * pyobject = "PyErr_Fetch_wrapper" external pyerr_restore_internal: pyobject -> pyobject -> pyobject -> unit = "PyErr_Restore_wrapper" external pystring_asstringandsize: pyobject -> string option = "PyString_AsStringAndSize_wrapper" external pyobject_ascharbuffer: pyobject -> string option = "PyObject_AsCharBuffer_wrapper" external pyobject_asreadbuffer: pyobject -> string option = "PyObject_AsReadBuffer_wrapper" external pyobject_aswritebuffer: pyobject -> string option = "PyObject_AsWriteBuffer_wrapper" external pylong_fromstring: string -> int -> pyobject * int = "PyLong_FromString_wrapper" external pycapsule_isvalid: Pytypes.pyobject -> string -> int = "Python27_PyCapsule_IsValid_wrapper" external pycapsule_check: Pytypes.pyobject -> int = "pyml_capsule_check" external pyframe_new : string -> string -> int -> Pytypes.pyobject = "pyml_pyframe_new" external ucs: unit -> ucs = "py_get_UCS" (* Avoid warning 32. *) let () = ignore (UCSNone, UCS2, UCS4) let initialized = ref false let is_initialized () = !initialized let assert_initialized () = if not !initialized then failwith "Py.assert_initialized: run 'Py.initialize ()' first" let version_value = ref "" let version_major_value = ref 0 let version_minor_value = ref 0 let program_name = ref Sys.argv.(0) let set_program_name s = program_name := s; if !initialized then if !version_major_value <= 2 then Pywrappers.Python2.py_setprogramname s else Pywrappers.Python3.py_setprogramname s let python_home = ref None let pythonpaths = ref [] let set_python_home s = python_home := (Some s); if !initialized then if !version_major_value <= 2 then Pywrappers.Python2.py_setpythonhome s else Pywrappers.Python3.py_setpythonhome s let add_python_path path = pythonpaths := path :: !pythonpaths let extract_version version_line = let before = try String.index version_line ' ' with Not_found -> let msg = Printf.sprintf "Py.extract_version: cannot parse the version line '%s'" version_line in failwith msg in Pyutils.split_left_on_char ~from:(succ before) ' ' version_line let extract_version_major_minor version = try if String.length version >= 3 && version.[1] = '.' then let major = int_of_string (String.sub version 0 1) in let minor = if String.length version = 3 || version.[3] = '.' then int_of_string (String.sub version 2 1) else if String.length version >= 5 && version.[4] = '.' then int_of_string (String.sub version 2 2) else raise Exit in (major, minor) else raise Exit with Exit | Failure _ -> let msg = Printf.sprintf "Py.extract_version_major_minor:\ unable to parse the version number '%s'" version in failwith msg let run_command ?(input = "") command read_stderr = let (input_channel, output, error) = Unix.open_process_full command (Unix.environment ()) in let result = try output_string output input; close_out output; Pyutils.input_lines (if read_stderr then error else input_channel) with _ -> begin try ignore (Unix.close_process_full (input_channel, output, error)) with _ -> () end; let msg = Printf.sprintf "Py.run_command: unable to read the result of '%s'" command in failwith msg in if Unix.close_process_full (input_channel, output, error) <> Unix.WEXITED 0 then begin let msg = Printf.sprintf "Py.run_command: unable to run '%s'" command in failwith msg; end; result let run_command_opt ?input command read_stderr = try Some (run_command ?input command read_stderr) with Failure _ -> None let parent_dir filename = let dirname = Filename.dirname filename in Filename.concat dirname Filename.parent_dir_name let has_putenv = ref false let has_set_pythonpath = ref None let init_pythonhome verbose pythonhome = pythonhome <> "" && try ignore (Sys.getenv "PYTHONHOME"); false with Not_found -> if verbose then begin Printf.eprintf "Temporary set PYTHONHOME=\"%s\".\n" pythonhome; flush stderr; end; Unix.putenv "PYTHONHOME" pythonhome; has_putenv := true; true let uninit_pythonhome () = if !has_putenv then begin unsetenv "PYTHONHOME"; has_putenv := false end let uninit_pythonpath () = match !has_set_pythonpath with None -> () | Some old_pythonpath -> begin has_set_pythonpath := None; match old_pythonpath with None -> unsetenv "PYTHONPATH" | Some old_pythonpath' -> Unix.putenv "PYTHONPATH" old_pythonpath' end let ldd executable = let command = match Pyml_arch.os with | Pyml_arch.Mac -> Printf.sprintf "otool -L %s" executable | _ -> Printf.sprintf "ldd %s" executable in match run_command_opt command false with None -> [] | Some lines -> let extract_line line = String.trim (Pyutils.split_left_on_char '(' (Pyutils.split_right_on_char '>' line)) in List.map extract_line lines let ldconfig () = match run_command_opt "ldconfig -p" false with None -> [] | Some lines -> let extract_line line = String.trim (Pyutils.split_right_on_char '>' line) in List.map extract_line lines let libpython_from_interpreter python_full_path = let lines = ldd python_full_path in let is_libpython line = let basename = Filename.basename line in Stdcompat.String.starts_with ~prefix:"libpython" basename in List.find_opt is_libpython lines let libpython_from_ldconfig major minor = let lines = ldconfig () in let prefix = match major, minor with None, _ -> "libpython" | Some major', None -> Printf.sprintf "libpython%d" major' | Some major', Some minor' -> Printf.sprintf "libpython%d.%d" major' minor' in let is_libpython line = let basename = Filename.basename line in Stdcompat.String.starts_with ~prefix:prefix basename in List.find_opt is_libpython lines let parse_python_list list = let length = String.length list in let buffer = Buffer.create 17 in let rec parse_item accu index = if index < length then match list.[index] with '\'' -> begin let item = Buffer.contents buffer in let accu = item :: accu in if index + 1 < length then match list.[index + 1] with ']' -> if index + 2 = length then Some (List.rev accu) else None | ',' -> if list.[index + 2] = ' ' && list.[index + 3] = '\'' then begin Buffer.clear buffer; parse_item accu (index + 4) end else None | _ -> None else None end | '\\' -> if index + 1 < length then begin match list.[index + 1] with '\n' -> parse_item accu (index + 2) | '0' .. '9' -> if index + 3 < length then begin let octal_number = String.sub list (index + 1) 3 in let c = char_of_int (Pyutils.int_of_octal octal_number) in Buffer.add_char buffer c; parse_item accu (index + 4) end else None | 'x' -> if index + 2 < length then begin let hexa_number = String.sub list (index + 1) 2 in let c = char_of_int (Pyutils.int_of_hex hexa_number) in Buffer.add_char buffer c; parse_item accu (index + 3) end else None | c -> begin match try let c' = match c with '\\' -> '\\' | '\'' -> '\'' | '"' -> '"' | 'a' -> '\007' | 'b' -> '\b' | 'f' -> '\012' | 'n' -> '\n' | 'r' -> '\r' | 't' -> '\t' | 'v' -> '\011' | _ -> raise Not_found in Some c' with Not_found -> None with None -> None | Some c' -> Buffer.add_char buffer c'; parse_item accu (index + 2) end end else None | c -> Buffer.add_char buffer c; parse_item accu (index + 1) else None in if length >= 2 && list.[0] == '[' then match list.[1] with '\'' -> Buffer.clear buffer; parse_item [] 2 | ']' when length = 2 -> Some [] | _ -> None else None let pythonpaths_from_interpreter python_full_path = let command = "\ import sys print(sys.path) " in match try run_command ~input:command python_full_path false with Failure _ -> [] with [path_line] -> begin match parse_python_list path_line with None -> [] | Some paths -> paths end | _ -> [] let concat_library_filenames library_paths library_filenames = let expand_filepaths filename = filename :: List.map (fun path -> Filename.concat path filename) library_paths in List.concat (List.map expand_filepaths library_filenames) let library_suffix = match Pyml_arch.os with | Pyml_arch.Mac -> ".dylib" | _ -> ".so" let libpython_from_pkg_config version_major version_minor = let command = Printf.sprintf "pkg-config --libs python-%d.%d" version_major version_minor in match run_command_opt command false with Some (words :: _) -> let word_list = String.split_on_char ' ' words in let unable_to_parse () = let msg = Printf.sprintf "Py.find_library_path: unable to parse the output of pkg-config '%s'" words in failwith msg in let parse_word (library_paths, library_filename) word = if String.length word > 2 then match String.sub word 0 2 with "-L" -> let word' = Pyutils.substring_between word 2 (String.length word) in (word' :: library_paths, library_filename) | "-l" -> let word' = Pyutils.substring_between word 2 (String.length word) in if library_filename <> None then unable_to_parse (); let library_filename = Printf.sprintf "lib%s%s" word' library_suffix in (library_paths, Some library_filename) | _ -> (library_paths, library_filename) else (library_paths, library_filename) in let (library_paths, library_filename) = List.fold_left parse_word ([], None) word_list in let library_filename = match library_filename with None -> unable_to_parse () | Some library_filename -> library_filename in Some (concat_library_filenames library_paths [library_filename]) | _ -> None let library_patterns : (int -> int -> string) list = match Pyml_arch.os with | 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 library_filenames_from_paths version_major version_minor paths = let library_filenames = List.map (fun format -> format version_major version_minor) library_patterns in concat_library_filenames paths library_filenames let libpython_from_python_config version_major version_minor = let command = Printf.sprintf "python%d.%d-config --ldflags" version_major version_minor in match run_command_opt command false with | Some (words :: _) -> let word_list = String.split_on_char ' ' words in let parse_word library_paths word = if String.length word > 2 then match String.sub word 0 2 with "-L" -> let word' = Pyutils.substring_between word 2 (String.length word) in word' :: library_paths | _ -> library_paths else library_paths in let library_paths = List.fold_left parse_word [] word_list in Some (library_filenames_from_paths version_major version_minor library_paths) | _ -> None let libpython_from_python_config_prefix version_major version_minor = let command = Printf.sprintf "python%d.%d-config --prefix" version_major version_minor in match run_command_opt command false with | Some (prefix :: _) -> let library_paths = [Filename.concat prefix "lib"] in Some (library_filenames_from_paths version_major version_minor library_paths) | _ -> None let getenv_opt var = try Some (Sys.getenv var) with Not_found -> None let libpython_from_pythonhome version_major version_minor python_full_path = let library_paths = match match getenv_opt "PYTHONHOME" with | Some python_home -> Some (Pyutils.split_left_on_char ':' python_home) | None -> match python_full_path with | Some python_full_path -> Some (parent_dir python_full_path) | None -> None with None -> failwith "Unable to find libpython!" | Some dir -> [Filename.concat dir "lib"] in library_filenames_from_paths version_major version_minor library_paths let libpython_from_pythonpath version_major version_minor = match getenv_opt "PYTHONPATH" with | None -> None | Some pythonpath -> let paths = String.split_on_char ':' pythonpath in let python_zip = Printf.sprintf "python%d%d.zip" version_major version_minor in let is_python_zip filename = Filename.basename filename = python_zip in match List.find_opt is_python_zip paths with | None -> None | Some filename -> let dir = Filename.dirname filename in Some (library_filenames_from_paths version_major version_minor [dir]) let find_library_path version_major version_minor python_full_path = let heuristics = [ (fun () -> Option.bind python_full_path (fun path -> Option.map (fun path -> [path]) (libpython_from_interpreter path))); (fun () -> Option.map (fun path -> [path]) (libpython_from_ldconfig version_major version_minor)); (fun () -> Option.bind version_major (fun version_major -> Option.bind version_minor (fun version_minor -> libpython_from_pkg_config version_major version_minor))); (fun () -> Option.bind version_major (fun version_major -> Option.bind version_minor (fun version_minor -> libpython_from_python_config_prefix version_major version_minor))); (fun () -> Option.bind version_major (fun version_major -> Option.bind version_minor (fun version_minor -> libpython_from_python_config version_major version_minor))); (fun () -> Option.bind version_major (fun version_major -> Option.bind version_minor (fun version_minor -> Some (libpython_from_pythonhome version_major version_minor python_full_path)))); (fun () -> Option.bind version_major (fun version_major -> Option.bind version_minor (fun version_minor -> libpython_from_pythonpath version_major version_minor))); ] in List.concat (List.map (fun f -> Option.value ~default:[] (f ())) heuristics) let python_version_from_interpreter interpreter = let version_line = let python_version_cmd = Printf.sprintf "\"%s\" --version" interpreter in try List.hd (run_command python_version_cmd false) with Failure _ -> List.hd (run_command python_version_cmd true) in extract_version version_line let library_filename = ref None let load_library filename = library_filename := 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 with Failure _ -> let library_filenames = find_library_path version_major version_minor python_full_path in let errors = Buffer.create 17 in let rec try_load_library library_filenames = match library_filenames with [] -> let msg = Printf.sprintf "Py.find_library: unable to find the Python library%s" (Buffer.contents errors) in failwith msg | filename :: others -> begin (* let pythonhome_set = not (Filename.is_implicit filename) && init_pythonhome verbose (parent_dir filename) in *) try if verbose then begin Printf.eprintf "Trying to load \"%s\".\n" filename; flush stderr; end; load_library (Some filename); with Failure msg -> (* if pythonhome_set then uninit_pythonhome (); *) if verbose then begin Printf.eprintf "Failed: \"%s\".\n" msg; flush stderr; end; Printf.bprintf errors " [%s returned %s]" filename msg; try_load_library others end in try_load_library library_filenames let initialize_library ~verbose ~version_major ~version_minor ~debug_build python_full_path = begin match !python_home with None -> () | Some s -> ignore (init_pythonhome verbose s) end; find_library ~verbose ~version_major ~version_minor ~debug_build python_full_path; (* begin match python_full_path with None -> () | Some python_full_path' -> let pythonhome = let dirname = Filename.dirname python_full_path' in if Filename.basename dirname = "bin" then Filename.concat dirname Filename.parent_dir_name else dirname in ignore (init_pythonhome verbose pythonhome); end; *) set_program_name !program_name; begin match !python_home with None -> () | Some s -> set_python_home s end let get_version = Pywrappers.py_getversion let which_command = match Pyml_arch.os with | Pyml_arch.Windows -> "where" | _ -> "command -v" let which program = let exe = match Pyml_arch.os with | Pyml_arch.Windows -> if Filename.check_suffix program ".exe" then program else program ^ ".exe" | _ -> program in let command = Printf.sprintf "%s \"%s\"" which_command exe in match run_command_opt command false with Some (path :: _) -> Some path | _ -> None let find_interpreter interpreter version minor = match interpreter with Some interpreter' -> if String.contains interpreter' '/' then Some interpreter' else which interpreter' | None -> match Option.bind version (fun version' -> match Option.bind minor (fun minor' -> which (Printf.sprintf "python%d.%d" version' minor')) with | Some result -> Some result | None -> which (Printf.sprintf "python%d" version')) with | Some result -> Some result | None -> match which "python" with | Some result -> Some result | None -> match which "python3" with | Some result -> Some result | None -> None let version_mismatch interpreter found expected = Printf.sprintf "Version mismatch: %s is version %s but version %s is expected" interpreter found expected let build_version_string major minor = Printf.sprintf "%d.%d" major minor let path_separator = match Pyml_arch.os with | Pyml_arch.Windows -> ";" | _ -> ":" (* 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"; let do_initialize () = match library_name with | Some library_name -> load_library (Some library_name); | None -> 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 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 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 version_value := version; version_major_value := version_major; version_minor_value := version_minor; initialized := true let on_finalize_list = ref [] let on_finalize f = on_finalize_list := 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 (); initialized := false let version () = assert_initialized (); !version_value let version_major () = assert_initialized (); !version_major_value let version_minor () = assert_initialized (); !version_minor_value let version_pair () = assert_initialized (); (!version_major_value, !version_minor_value) let null = pynull () let is_null v = v == null let none = pynone () let is_none v = v == none exception E of pyobject * pyobject 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 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 python_exception (); result let check_some s = match s with None -> python_exception () | Some s -> s let check_error () = if Pywrappers.pyerr_occurred () <> null then python_exception () let check_int result = if result = -1 then python_exception () else result let check_int64 result = if result = -1L then python_exception () else result let assert_int_success result = if result = -1 then python_exception () let bool_of_int i = check_int i <> 0 let get_program_name () = if !initialized then if !version_major_value <= 2 then Pywrappers.Python2.py_getprogramname () else Pywrappers.Python3.py_getprogramname () else !program_name let get_python_home () = if !initialized then if !version_major_value <= 2 then Pywrappers.Python2.py_getpythonhome () else Pywrappers.Python3.py_getpythonhome () else match !python_home with None -> "" | Some s -> s let get_program_full_path () = if version_major () <= 2 then Pywrappers.Python2.py_getprogramfullpath () else Pywrappers.Python3.py_getprogramfullpath () let get_prefix () = if version_major () <= 2 then Pywrappers.Python2.py_getprogramfullpath () else Pywrappers.Python3.py_getprogramfullpath () let get_exec_prefix () = if version_major () <= 2 then Pywrappers.Python2.py_getexecprefix () else Pywrappers.Python3.py_getexecprefix () let get_path () = if version_major () <= 2 then Pywrappers.Python2.py_getpath () else Pywrappers.Python3.py_getpath () let get_platform = Pywrappers.py_getplatform let get_copyright = Pywrappers.py_getcopyright let get_compiler = Pywrappers.py_getcompiler let get_build_info = Pywrappers.py_getbuildinfo let option result = if result = null then begin check_error (); None end else Some result let check_found result = if result = null then begin check_error (); raise Not_found end else result let option_of_error result = if result = null then begin let _ = pyerr_fetch_internal () in None end 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 = call_object_with_keywords func arg null let get_builtins () = check_not_null (Pywrappers.pyeval_getbuiltins ()) let get_globals () = check_not_null (Pywrappers.pyeval_getglobals ()) let get_locals () = check_not_null (Pywrappers.pyeval_getlocals ()) end let object_repr obj = check_not_null (Pywrappers.pyobject_repr obj) 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 | UCS4 -> Pywrappers.UCS4.pyunicodeucs4_asutf8string | UCSNone -> if !version_major_value >= 3 then Pywrappers.Python3.pyunicode_asutf8string else failwith "String.as_UTF8_string: unavailable" in check_not_null (f s) let of_string s = let len = String.length s in if !version_major_value >= 3 then check_not_null (Pywrappers.Python3.pyunicode_fromstringandsize s len) else check_not_null (Pywrappers.Python2.pystring_fromstringandsize s len) let of_bytes s = of_string (Bytes.unsafe_to_string s) end module Tuple_ = struct let create size = check_not_null (Pywrappers.pytuple_new size) let set_item s index value = assert_int_success (Pywrappers.pytuple_setitem s index value) let set = set_item let init size f = let result = create size in for index = 0 to size - 1 do let v = f index in assert_not_null "init" v; set_item result index v done; result let of_array array = init (Array.length array) (Array.get array) let of_list list = of_array (Array.of_list list) end let id x = x module Dict_ = struct let create () = 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 = let result = create () in List.iter begin fun (key, value) -> set_item result (fkey key) (fvalue value); end list; result 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 let call_function_obj_args callable args = check_not_null (pyobject_callfunctionobjargs callable args) end module Type = struct (* We rely on physical equality to check if an object is none as [pyml_wrap] ensures that the same ocaml value is always used to represent [None]. *) let is_none v = v == none let none = None type t = Unknown | Bool | Bytes | Callable | Capsule | Closure | Dict | Float | List | Int | Long | Module | None | Null | Tuple | Type | Unicode | Iter | Set 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 = match t with Unknown -> "Unknown" | Bool -> "Bool" | Bytes -> "Bytes" | Callable -> "Callable" | Capsule -> "Capsule" | Closure -> "Closure" | Dict -> "Dict" | Float -> "Float" | List -> "List" | Int -> "Int" | Long -> "Long" | Module -> "Module" | None -> "None" | Null -> "Null" | Tuple -> "Tuple" | Type -> "Type" | Unicode -> "Unicode" | Iter -> "Iter" | Set -> "Set" let to_string s = match get s with Bytes -> Some (pystring_asstringandsize s) | Unicode -> Some (pystring_asstringandsize (String_.as_UTF8_string s)) | _ -> none let string_of_repr item = match to_string (object_repr item) with Some repr -> check_some repr | _ (* None *) -> failwith "Py.Object.string_of_repr" let mismatch t o = failwith (Printf.sprintf "Type mismatch: %s expected. Got: %s (%s)" t (name (get o)) (string_of_repr o)) let create classname parents dict = let ty = Pywrappers.pytype_type () in let classname = String_.of_string classname in let parents = Tuple_.of_list parents in let dict = Dict_.of_bindings_string dict in Object_.call_function_obj_args ty [| classname; parents; dict |] 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" let table = Hashtbl.create 17 let () = on_finalize (fun () -> Hashtbl.clear table) external unsafe_wrap_value: 'a -> pyobject = "pyml_wrap_value" external unsafe_unwrap_value: pyobject -> 'a = "pyml_unwrap_value" let make name = try Hashtbl.find table name; failwith (Printf.sprintf "Py.Capsule.make: capsule of type %s already defined" name) with Not_found -> Hashtbl.add table name (); let wrap v = unsafe_wrap_value (name, v) in let unwrap x = if pycapsule_check x = 0 then Type.mismatch "capsule" x; let name', v = unsafe_unwrap_value x in if name <> name' then failwith (Printf.sprintf "Py.Capsule: capsule of type %s, but type %s expected" name' name); 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; fst (unsafe_unwrap_value x) end module Mapping = struct let check v = Pywrappers.pymapping_check v <> 0 let get_item_string mapping key = option (Pywrappers.pymapping_getitemstring mapping key) let find_string mapping key = check_found (Pywrappers.pymapping_getitemstring mapping key) let find_string_opt = get_item_string let has_key mapping key = Pywrappers.pymapping_haskey mapping key <> 0 let has_key_string mapping key = Pywrappers.pymapping_haskeystring mapping key <> 0 let length mapping = check_int (Pywrappers.pymapping_length mapping) let set_item_string mapping key value = assert_int_success (Pywrappers.pymapping_setitemstring mapping key value) let size mapping = check_int (Pywrappers.pymapping_size mapping) 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 = 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 let t = pytrue () let is_true v = v == t let f = pyfalse () let is_false v = v == f let check v = v = t || v = f let of_bool b = if b then t else f let to_bool v = if v = t then true else if v = f then false else Type.mismatch "True or False" v end module Float = struct let check o = Type.get o = Type.Float let of_float = Pywrappers.pyfloat_fromdouble let to_float v = let result = Pywrappers.pyfloat_asdouble v in if result = -1.0 then check_error (); result end type byteorder = LittleEndian | BigEndian let string_length = String.length module String__ = struct include String_ let check_bytes s = Type.get s = Type.Bytes let check_unicode s = Type.get s = Type.Unicode let check s = match Type.get s with Type.Bytes | Type.Unicode -> true | _ -> false let decode_UTF8 ?errors ?size s = let size' = match size with None -> String.length s | Some size' -> size' in let f = match ucs () with UCS2 -> Pywrappers.UCS2.pyunicodeucs2_decodeutf8 | UCS4 -> Pywrappers.UCS4.pyunicodeucs4_decodeutf8 | UCSNone -> if !version_major_value >= 3 then Pywrappers.Python3.pyunicode_decodeutf8 else failwith "Py.String.decode_UTF8: unavailable" in check_not_null (f s size' errors) let decode_UTF16_32 decode_ucs2 decode_ucs4 decode_python3 errors size byteorder s = let size' = match size with None -> String.length s | Some size' -> size' in let byteorder' = match byteorder with None -> 0 | Some LittleEndian -> -1 | Some BigEndian -> 1 in let byteorder_ref = ref byteorder' in let f = match ucs () with UCS2 -> decode_ucs2 | UCS4 -> decode_ucs4 | UCSNone -> if !version_major_value >= 3 then decode_python3 else failwith "Py.String.decode_UTF16/32: unavailable" in let decoded_string = check_not_null (f s size' errors byteorder_ref) in let decoded_byteorder = match !byteorder_ref with -1 -> LittleEndian | 1 -> BigEndian | _ -> failwith "Py.String.decode_UTF16/32: unknown endianess value" in (decoded_string, decoded_byteorder) let decode_UTF16 ?errors ?size ?byteorder s = decode_UTF16_32 Pywrappers.UCS2.pyunicodeucs2_decodeutf16 Pywrappers.UCS4.pyunicodeucs4_decodeutf16 Pywrappers.Python3.pyunicode_decodeutf16 errors size byteorder s let decode_UTF32 ?errors ?size ?byteorder s = decode_UTF16_32 Pywrappers.UCS2.pyunicodeucs2_decodeutf32 Pywrappers.UCS4.pyunicodeucs4_decodeutf32 Pywrappers.Python3.pyunicode_decodeutf32 errors size byteorder s let of_unicode ?size int_array = let size' = match size with None -> Array.length int_array | Some size' -> size' in let f = match ucs () with UCS2 -> Pywrappers.UCS2.pyunicodeucs2_fromunicode | UCS4 -> Pywrappers.UCS4.pyunicodeucs4_fromunicode | UCSNone -> if !version_major_value >= 3 then Pywrappers.Python3.pyunicode_fromkindanddata 4 else failwith "Py.String.of_unicode: unavailable" in 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 | UCS4 -> Pywrappers.UCS4.pyunicodeucs4_asunicode | UCSNone -> if !version_major_value >= 3 then Pywrappers.Python3.pyunicode_asucs4copy else failwith "Py.String.to_unicode: unavailable" in check_some (f s) let string_type_mismatch obj = Type.mismatch "String or Unicode" obj let format fmt args = match Type.get fmt with Type.Unicode -> let f = match ucs () with UCS2 -> Pywrappers.UCS2.pyunicodeucs2_format | UCS4 -> Pywrappers.UCS4.pyunicodeucs4_format | UCSNone -> if !version_major_value >= 3 then Pywrappers.Python3.pyunicode_format else failwith "Py.String.format: unavailable" in check_not_null (f fmt args) | Type.Bytes -> if !version_major_value >= 3 then failwith "No format on Bytes in Python 3" else check_not_null (Pywrappers.Python2.pystring_format fmt args) | _ -> string_type_mismatch fmt let length s = match Type.get s with Type.Unicode -> let f = match ucs () with UCS2 -> Pywrappers.UCS2.pyunicodeucs2_getsize | UCS4 -> Pywrappers.UCS4.pyunicodeucs4_getsize | UCSNone -> if !version_major_value >= 3 then Pywrappers.Python3.pyunicode_getlength else failwith "Py.String.length: unavailable" in f s | Type.Bytes -> if !version_major_value >= 3 then Pywrappers.Python3.pybytes_size s else Pywrappers.Python2.pystring_size s | _ -> string_type_mismatch s let to_string s = match Type.to_string s with None -> string_type_mismatch s | Some s -> check_some s let to_bytes s = Bytes.unsafe_of_string (to_string s) end module Bytes = struct include String__ let of_string s = let len = String.length s in if !version_major_value >= 3 then check_not_null (Pywrappers.Python3.pybytes_fromstringandsize s len) else check_not_null (Pywrappers.Python2.pystring_fromstringandsize s len) let of_bytes s = of_string (Bytes.unsafe_to_string s) end module String = String__ module Err = struct type t = Exception | StandardError | ArithmeticError | LookupError | AssertionError | AttributeError | EOFError | EnvironmentError | FloatingPointError | IOError | ImportError | IndexError | KeyError | KeyboardInterrupt | MemoryError | NameError | NotImplementedError | OSError | OverflowError | ReferenceError | RuntimeError | SyntaxError | SystemExit | TypeError | ValueError | ZeroDivisionError | StopIteration let clear () = Pywrappers.pyerr_clear (); fetched_exception := None let exception_matches exc = Pywrappers.pyerr_exceptionmatches exc <> 0 let fetch () = let (ptype, pvalue, ptraceback) = pyerr_fetch_internal () in if ptype = null then None else Some (ptype, pvalue, ptraceback) let fetched () = !fetched_exception let given_exception_matches given exc = Pywrappers.pyerr_givenexceptionmatches given exc <> 0 let occurred () = option (Pywrappers.pyerr_occurred ()) let print () = Pywrappers.pyerr_print () let print_ex i = Pywrappers.pyerr_printex i let restore = pyerr_restore_internal let restore_tuple (ptype, pvalue, ptraceback) = restore ptype pvalue ptraceback let restore_fetch () = match fetch () with Some tuple -> restore_tuple tuple | None -> failwith "restore_fetch" let restore_fetched () = match fetched () with Some tuple -> restore_tuple tuple | None -> failwith "restore_fetched" let set_none = Pywrappers.pyerr_setnone let set_string = Pywrappers.pyerr_setstring let set_object = Pywrappers.pyerr_setobject let of_error = function Exception -> Pywrappers.pyexc_exception () | StandardError -> if !version_major_value <= 2 then Pywrappers.Python2.pyexc_standarderror () else Pywrappers.pyexc_exception () | ArithmeticError -> Pywrappers.pyexc_arithmeticerror () | LookupError -> Pywrappers.pyexc_lookuperror () | AssertionError -> Pywrappers.pyexc_assertionerror () | AttributeError -> Pywrappers.pyexc_attributeerror () | EOFError -> Pywrappers.pyexc_eoferror () | EnvironmentError -> Pywrappers.pyexc_environmenterror () | FloatingPointError -> Pywrappers.pyexc_floatingpointerror () | IOError -> Pywrappers.pyexc_ioerror () | ImportError -> Pywrappers.pyexc_importerror () | IndexError -> Pywrappers.pyexc_indexerror () | KeyError -> Pywrappers.pyexc_keyerror () | KeyboardInterrupt -> Pywrappers.pyexc_keyboardinterrupt () | MemoryError -> Pywrappers.pyexc_memoryerror () | NameError -> Pywrappers.pyexc_nameerror () | NotImplementedError -> Pywrappers.pyexc_notimplementederror () | OSError -> Pywrappers.pyexc_oserror () | OverflowError -> Pywrappers.pyexc_overflowerror () | ReferenceError -> Pywrappers.pyexc_referenceerror () | RuntimeError -> Pywrappers.pyexc_runtimeerror () | SyntaxError -> Pywrappers.pyexc_syntaxerror () | SystemExit -> Pywrappers.pyexc_systemerror () | TypeError -> Pywrappers.pyexc_typeerror () | ValueError -> Pywrappers.pyexc_valueerror () | ZeroDivisionError -> Pywrappers.pyexc_zerodivisionerror () | StopIteration -> Pywrappers.pyexc_stopiteration () 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 let attribute_error = "AttributeError" let check_found_catch error result = try check_found result with E (ty, _) when String.to_string (check_found (Pywrappers.pyobject_getattrstring ty "__name__")) = error -> raise Not_found module Object = struct include Object_ 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 find_attr_string obj attr = assert_not_null "find_attr_string" obj; check_found_catch attribute_error (Pywrappers.pyobject_getattrstring obj attr) let find_attr_string_err obj attr = assert_not_null "find_attr_string" obj; check_not_null (Pywrappers.pyobject_getattrstring obj attr) let get_attr_string obj attr = assert_not_null "find_attr_string" obj; option_of_error (Pywrappers.pyobject_getattrstring obj attr) let find_attr obj attr = assert_not_null "find_attr(!, _)" obj; assert_not_null "find_attr(_, !)" attr; check_found_catch attribute_error (Pywrappers.pyobject_getattr obj attr) let find_attr_err obj attr = assert_not_null "find_attr(!, _)" obj; assert_not_null "find_attr(_, !)" attr; check_not_null (Pywrappers.pyobject_getattr obj attr) let find_attr_opt = get_attr let find_attr_string_opt = get_attr_string let get_item obj key = option (Pywrappers.pyobject_getitem obj key) let find obj attr = check_found_catch "KeyError" (Pywrappers.pyobject_getitem obj attr) let find_err obj attr = check_not_null (Pywrappers.pyobject_getitem obj attr) let find_opt = get_item let get_item_string obj key = get_item obj (String.of_string key) let find_string obj key = find obj (String.of_string key) let find_string_err obj key = find_err obj (String.of_string key) 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 = 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 = assert_int_success (Pywrappers.pyobject_print obj (Pytypes.file_map Unix.descr_of_out_channel out_channel) 1) let repr = object_repr let rich_compare a b cmp = check_not_null (Pywrappers.pyobject_richcompare a b cmp) let rich_compare_bool a b cmp = 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 let del_attr_string obj attr = set_attr_string obj attr null let set_item obj key value = assert_int_success (Pywrappers.pyobject_setitem obj key value) let set_item_string obj key value = set_item obj (String.of_string key) value let str obj = check_not_null (Pywrappers.pyobject_str obj) let string_of_repr = Type.string_of_repr let to_string item = String.to_string (str item) let as_char_buffer obj = check_some (pyobject_ascharbuffer obj) let as_read_buffer obj = check_some (pyobject_asreadbuffer obj) let as_write_buffer obj = check_some (pyobject_aswritebuffer obj) external reference_count: pyobject -> int = "pyrefcount" let repr_or_string repr v = if repr then string_of_repr v else to_string v let robust_to_string repr v = if !initialized then try try repr_or_string repr v with E (_ty, _value) -> repr_or_string (Stdlib.not repr) v with E (ty, value) -> Printf.sprintf "[ERROR] %s: %s" (to_string ty) (to_string value) else "" let format fmt v = Format.pp_print_string fmt (robust_to_string false v) let format_repr fmt v = 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) let dir obj = assert_not_null "dir" obj; check_not_null (Pywrappers.pyobject_dir obj) end let exception_printer exn = match exn with E (ty, value) when !initialized -> Some ( Printf.sprintf "E (%s, %s)" (Object.to_string ty) (Object.to_string value)) | _ -> None let () = Stdlib_printexc.register_printer exception_printer module Long = struct let check o = Type.get o = Type.Long let of_int64 v = check_not_null (Pywrappers.pylong_fromlong v) let to_int64 v = let result = Pywrappers.pylong_aslong v in check_error (); result let of_int v = of_int64 (Int64.of_int v) let to_int v = Int64.to_int (to_int64 v) let from_string str base = let result = pylong_fromstring str base in ignore (check_not_null (fst result)); result let of_string ?(base = 0) s = let value, len = from_string s base in if len <> string_length s then failwith "Py.Long.of_string"; value let to_string = Object.to_string end module Int = struct let check o = Type.get o = Type.Long let of_int64 v = if version_major () >= 3 then Long.of_int64 v else check_not_null (Pywrappers.Python2.pyint_fromlong v) let to_int64 v = if version_major () >= 3 then Long.to_int64 v else let result = Pywrappers.Python2.pyint_aslong v in check_error (); result let of_int v = of_int64 (Int64.of_int v) let to_int v = Int64.to_int (to_int64 v) let of_string = Long.of_string let to_string = Long.to_string end module Number = struct let absolute v = check_not_null (Pywrappers.pynumber_absolute v) 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 = 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 = 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 = match Type.get v with Type.Float | Type.Long -> true | _ -> false let of_int i = Int.of_int i let of_int64 i = Int.of_int64 i let of_float f = Float.of_float f let to_float v = match Type.get v with Type.Float -> Float.to_float v | Type.Long -> Int64.to_float (Long.to_int64 v) | _ -> Type.mismatch "Long or Float" v let ( + ) = add let ( - ) = subtract let ( * ) = multiply let ( / ) = true_divide let ( ** ) x y = power x y let ( land ) = number_and let ( lor ) = number_or let ( lxor ) = number_xor let ( lsl ) = lshift let ( lsr ) = rshift let ( ~- ) = negative end module Iter_ = struct let check o = Type.get o = Type.Iter let next i = assert_not_null "next" i; option (Pywrappers.pyiter_next i) let rec iter f i = match next i with None -> () | Some item -> f item; iter f i let rec fold_left f v i = match next i with None -> v | Some item -> fold_left f (f v item) i let rec fold_right f i v = match next i with None -> v | Some item -> f item (fold_right f i v) let to_list i = List.rev (fold_left (fun list item -> item :: list) [] i) let to_list_map f i = List.rev (fold_left (fun list item -> f item :: list) [] i) let rec for_all p i = match next i with None -> true | Some item -> p item && for_all p i let rec exists p i = 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 *) let vec_to_seq length get v = let length = length v in let rec aux i () = if i = length then Seq.Nil else let x = get v i in Seq.Cons (x, aux (i + 1)) in aux 0 let vec_to_seqi length get v = let length = length v in let rec aux i () = if i = length then Seq.Nil else let x = get v i in Seq.Cons ((i, x), aux (i + 1)) in aux 0 module Sequence = struct let check obj = bool_of_int (Pywrappers.pysequence_check obj) let concat s s' = check_not_null (Pywrappers.pysequence_concat s s') 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) let get_item sequence index = Pywrappers.pysequence_getitem sequence index let get = get_item let get_slice s i0 i1 = check_not_null (Pywrappers.pysequence_getslice s i0 i1) let index s value = check_int (Pywrappers.pysequence_index s value) let in_place_concat s s' = check_not_null (Pywrappers.pysequence_inplaceconcat s s') let in_place_repeat s count = check_not_null (Pywrappers.pysequence_inplacerepeat s count) let length s = check_int (Pywrappers.pysequence_length s) let list sequence = check_not_null (Pywrappers.pysequence_list sequence) let repeat s count = check_not_null (Pywrappers.pysequence_repeat s count) let set_item s index value = assert_int_success (Pywrappers.pysequence_setitem s index value) let set = set_item let set_slice s i0 i1 value = assert_int_success (Pywrappers.pysequence_setslice s i0 i1 value) let size s = assert_not_null "size" s; check_int (Pywrappers.pysequence_size s) let tuple sequence = check_not_null (Pywrappers.pysequence_tuple sequence) let to_array sequence = Array.init (size sequence) (get_item sequence) let to_array_map f sequence = Array.init (size sequence) (fun index -> f (get_item sequence index)) let rec fold_right_upto f upto sequence v = if upto > 0 then let i = pred upto in fold_right_upto f i sequence (f (get_item sequence i) v) else v let fold_right f sequence v = fold_right_upto f (length sequence) sequence v let to_list sequence = fold_right (fun item list -> item :: list) sequence [] let to_list_map f sequence = fold_right (fun item list -> f item :: list) sequence [] let fold_left f v sequence = Iter_.fold_left f v (Object.get_iter sequence) let for_all p sequence = Iter_.for_all p (Object.get_iter sequence) let exists p sequence = Iter_.exists p (Object.get_iter sequence) let to_seq = vec_to_seq size get let to_seqi = vec_to_seqi size get end module Tuple = struct include Sequence include Tuple_ let check o = Type.get o = Type.Tuple let empty = pytuple_empty () let is_empty v = v == empty let get_slice tuple i0 i1 = check_not_null (Pywrappers.pytuple_getslice tuple i0 i1) let size tuple = check_int (Pywrappers.pytuple_size tuple) let of_array_map f array = init (Array.length array) (fun i -> f (Array.get array i)) let of_list_map f list = of_array_map f (Array.of_list list) let of_sequence = Sequence.tuple let of_seq s = of_array (Array.of_seq s) let of_tuple1 v0 = init 1 (function _ -> v0) 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_tuple4 (v0, v1, v2, v3) = init 4 (function 0 -> v0 | 1 -> v1 | 2 -> v2 | _ -> v3) let of_tuple5 (v0, v1, v2, v3, v4) = init 5 (function 0 -> v0 | 1 -> v1 | 2 -> v2 | 3 -> v3 | _ -> v4) let to_tuple1 v = get_item v 0 let to_tuple2 v = (get_item v 0, get_item v 1) let to_tuple3 v = (get_item v 0, get_item v 1, get_item v 2) let to_tuple4 v = (get_item v 0, get_item v 1, get_item v 2, get_item v 3) let to_tuple5 v = (get_item v 0, get_item v 1, get_item v 2, get_item v 3, get_item v 4) let singleton = of_tuple1 let to_singleton = to_tuple1 let of_pair = of_tuple2 let to_pair = to_tuple2 end module Dict = struct include Dict_ let check o = Type.get o = Type.Dict 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 = assert_not_null "get_item(!, _)" dict; assert_not_null "get_item(_, !)" key; check_found (Pywrappers.pydict_getitem 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 = assert_not_null "get_item_string" dict; check_found (Pywrappers.pydict_getitemstring dict key) let find_string_opt = get_item_string let keys dict = check_not_null (Pywrappers.pydict_keys dict) let items dict = check_not_null (Pywrappers.pydict_items dict) let size dict = let sz = Pywrappers.pydict_size dict in assert_int_success sz; sz let values dict = check_not_null (Pywrappers.pydict_values dict) let iter f dict = Iter_.iter begin fun pair -> let (key, value) = Tuple.to_pair pair in f key value end (Object.get_iter (items dict)) let fold f dict v = Iter_.fold_left begin fun v pair -> let (key, value) = Tuple.to_pair pair in f key value v end v (Object.get_iter (items dict)) let for_all p dict = Iter_.for_all begin fun pair -> let (key, value) = Tuple.to_pair pair in p key value end (Object.get_iter (items dict)) let exists p dict = Iter_.exists begin fun pair -> let (key, value) = Tuple.to_pair pair in 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 (fkey key, fvalue value) end (Object.get_iter (items dict)) let to_bindings = to_bindings_map id id let to_bindings_string = to_bindings_map String.to_string id 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 o = assert_not_null "clear" o; assert_int_success (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 let of_list_map f list = let result = create () in List.iter begin fun value -> add result (f value) end list; result let of_list = of_list_map id end module Traceback = struct type frame = { filename : string ; function_name : string ; line_number : int } let create_frame { filename; function_name; line_number } = check_not_null (pyframe_new filename function_name line_number) type t = frame list let create t = let types_module = check_not_null (Pywrappers.pyimport_importmodule "types") in let tb_type = Object.find_attr_string types_module "TracebackType" in List.fold_left (fun acc frame -> let args = Tuple.of_array [| acc; create_frame frame; Int.of_int 0; Int.of_int frame.line_number |] in Object.call tb_type args null) none t end 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 let handle_errors f arg = try f arg with E (errtype, errvalue) -> Err.set_object errtype errvalue; null | Err (errtype, msg) | Err_with_traceback (errtype, msg, []) -> Err.set_error errtype msg; null | Err_with_traceback (errtype, msg, traceback) -> let () = (* Traceback objects can only be created since Python 3.7. *) if !version_major_value <= 2 || (!version_major_value == 3 && !version_minor_value < 7) then Err.set_error errtype msg else let traceback = Traceback.create traceback in 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 (WithoutKeywords (handle_errors f))) let of_function_as_tuple_and_dict ?name ?(docstring = "Anonymous closure") f = check_not_null (pywrap_closure name docstring (WithKeywords (fun args -> handle_errors (f args)))) let of_function ?name ?docstring f = of_function_as_tuple ?name ?docstring (fun args -> f (Tuple.to_array args)) let of_function_with_keywords ?name ?docstring f = of_function_as_tuple_and_dict ?name ?docstring (fun args dict -> f (Tuple.to_array args) dict) let to_function_as_tuple c = if not (check c) then Type.mismatch "Callable" c; function 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 -> Object.call c args keywords let to_function c = let f = to_function_as_tuple c in fun args -> f (Tuple.of_array args) let to_function_with_keywords c = let f = to_function_as_tuple_and_dict c in fun args keywords -> 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. let cleanup = Pywrappers.pyimport_cleanup *) 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 () = check_not_null (Pywrappers.pyimport_getmoduledict ()) let import_frozen_module name = bool_of_int (Pywrappers.pyimport_importfrozenmodule name) let import_module name = check_not_null (Pywrappers.pyimport_importmodule name) let import_module_opt name = try Some (check_not_null (Pywrappers.pyimport_importmodule name)) with E (e, _msg) when let ty = Object.to_string e in ty = "" || (* Python >=3.6*) ty = "" || (* Python <3.6 *) ty = "" (* Python 2 *) -> None let try_import_module = import_module_opt let import_module_level name globals locals fromlist level = check_not_null (Pywrappers.pyimport_importmodulelevel name globals locals fromlist level) let import_module_ex name globals locals fromlist = import_module_level name globals locals fromlist (-1) let reload_module obj = check_not_null (Pywrappers.pyimport_reloadmodule obj) end let import = Import.import_module let import_opt = Import.import_module_opt module Module = struct let check o = Type.get o = Type.Module let create name = 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_err let get_opt = Object.find_attr_string_opt let set = Object.set_attr_string let get_function m name = Callable.to_function (get m name) let get_function_opt m name = Option.map Callable.to_function (get_opt m name) let get_function_with_keywords m name = Callable.to_function_with_keywords (get m name) let get_function_with_keywords_opt m name = Option.map Callable.to_function_with_keywords (get_opt m name) let set_function m name f = set m name (Callable.of_function f) let set_function_with_keywords m name f = set m name (Callable.of_function_with_keywords f) let remove = Object.del_attr_string 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 end module Iter = struct include Iter_ let create next = let next_name = if version_major () >= 3 then "__next__" else "next" in let next' _args = match next () with None -> raise (Err (Err.StopIteration, "")) | Some item -> item in 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 Object.call_function_obj_args (Class.init ~methods "iterator") [| |] let of_seq s = let s = ref s in let next () = match !s () with | Seq.Nil -> None | Seq.Cons (head, tail) -> s := tail; Some head in create next let of_seq_map f s = let s = ref s in let next () = match !s () with | Seq.Nil -> None | Seq.Cons (head, tail) -> s := tail; Some (f head) in create next let to_seq i = let rec seq lazy_next () = match Lazy.force lazy_next with | None -> Seq.Nil | Some item -> Seq.Cons (item, seq (lazy (next i))) in seq (lazy (next i)) let to_seq_map f i = let rec seq lazy_next () = match Lazy.force lazy_next with | None -> Seq.Nil | Some item -> Seq.Cons (f item, seq (lazy (next i))) in seq (lazy (next i)) let unsafe_to_seq i = let rec seq () = match next i with | None -> Seq.Nil | Some item -> Seq.Cons (item, seq) in seq let of_list l = let l = ref l in let next () = match !l with | [] -> None | head :: tail -> l := tail; Some head in create next let of_list_map f l = let l = ref l in let next () = match !l with | [] -> None | head :: tail -> l := tail; Some (f head) in create next let seq_iter seq = 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. Only one such capsule is ever allocated. *) let sentinel = lazy (Callable.of_function_as_tuple (fun x -> x)) let create_call next = let sentinel = Lazy.force sentinel in let call = Callable.of_function_as_tuple (fun _pyobject -> match next () with | None -> sentinel | Some value -> value) in call_iter call sentinel end module List = struct include Sequence let check v = Type.get v = Type.List let create size = check_not_null (Pywrappers.pylist_new size) let size list = assert_not_null "size" list; check_int (Pywrappers.pylist_size list) let length = size let set_item list index value = assert_int_success (Pywrappers.pylist_setitem list index value) let set = set_item let init size f = let result = create size in for index = 0 to size - 1 do set result index (f index) done; result let of_array array = init (Array.length array) (Array.get array) let of_array_map f array = init (Array.length array) (fun i -> f (Array.get array i)) let of_list list = of_array (Array.of_list list) let of_list_map f list = of_array_map f (Array.of_list list) let of_sequence = Sequence.list let singleton v = assert_not_null "singleton" v; init 1 (fun _ -> v) let of_seq s = of_array (Array.of_seq s) end module Marshal = struct let version () = let marshal_module = Import.import_module "marshal" in Long.to_int (Module.get marshal_module "version") let read_object_from_file file = let fd = Pytypes.file_map Unix.descr_of_in_channel file in check_not_null (Pywrappers.pymarshal_readobjectfromfile fd) let load = read_object_from_file let read_last_object_from_file file = let fd = Pytypes.file_map Unix.descr_of_in_channel file in check_not_null (Pywrappers.pymarshal_readlastobjectfromfile fd) let read_object_from_string s len = check_not_null (Pywrappers.pymarshal_readobjectfromstring s len) let loads s = read_object_from_string s (string_length s) let write_object_to_file v file version = let fd = Pytypes.file_map Unix.descr_of_out_channel file in Pywrappers.pymarshal_writeobjecttofile v fd version let dump ?(version = version ()) v file = write_object_to_file v file version let write_object_to_string v version = check_not_null (Pywrappers.pymarshal_writeobjecttostring v version) let dumps ?(version = version ()) v = String.to_string (write_object_to_string v version) end module Array = struct let of_indexed_structure getter setter length = let methods = ["__len__", Callable.of_function_as_tuple (fun _tuple -> Int.of_int length); "__getitem__", Callable.of_function_as_tuple (fun tuple -> let (_self, key) = Tuple.to_tuple2 tuple in getter (Long.to_int key)); "__setitem__", Callable.of_function_as_tuple (fun tuple -> let (_self, key, value) = Tuple.to_tuple3 tuple in setter (Long.to_int key) value; none); "__iter__", Callable.of_function_as_tuple (fun _tuple -> let cursor = ref 0 in let next () = let index = !cursor in if index < length then begin cursor := succ index; Some (getter index) end else None in Iter.create next); "__repr__", Callable.of_function_as_tuple (fun tuple -> let (self) = Tuple.to_tuple1 tuple in Object.repr (Sequence.list self))] in Object.call_function_obj_args (Class.init ~methods "array") [| |] let of_array getter setter a = of_indexed_structure (fun i -> getter a.(i)) (fun i v -> a.(i) <- setter v) (Array.length a) type numpy_info = { numpy_api: Object.t; array_pickle: floatarray -> Object.t; array_unpickle: Object.t -> floatarray; pyarray_subtype: Object.t; } let numpy_info = ref None let () = on_finalize (fun () -> numpy_info := None) external get_pyarray_type: Object.t -> Object.t = "get_pyarray_type" external pyarray_of_floatarray: Object.t -> Object.t -> floatarray -> Object.t = "pyarray_of_floatarray_wrapper" external pyarray_move_floatarray: Object.t -> floatarray -> unit = "pyarray_move_floatarray_wrapper" let get_numpy_info () = match !numpy_info with Some info -> info | None -> let numpy_api = let numpy = Import.import_module "numpy.core.multiarray" in Object.find_attr_string numpy "_ARRAY_API" in let array_pickle, array_unpickle = Capsule.make "floatarray" in let pyarray_subtype = let pyarray_type = get_pyarray_type numpy_api in Type.create "ocamlarray" [pyarray_type] [("ocamlarray", none)] in let info = { numpy_api; array_pickle; array_unpickle; pyarray_subtype } in numpy_info := Some info; info let numpy_api () = (get_numpy_info ()).numpy_api let pyarray_type () = get_pyarray_type (numpy_api ()) let numpy_get_array a = let info = get_numpy_info () in info.array_unpickle (Object.find_attr_string a "ocamlarray") let clean_weak_ref weak_ref alarm_ref () = match Weak.get weak_ref 0 with | None -> begin match !alarm_ref with | None -> () | Some alarm -> Gc.delete_alarm alarm; alarm_ref := None end | Some numpy_array -> let array = numpy_get_array numpy_array in pyarray_move_floatarray numpy_array array let numpy a = let info = get_numpy_info () in let result = pyarray_of_floatarray info.numpy_api info.pyarray_subtype a in let result = check_not_null result in Object.set_attr_string result "ocamlarray" (info.array_pickle a); let weak_ref = Weak.create 1 in Weak.set weak_ref 0 (Some result); let alarm_ref = ref None in alarm_ref := Some (Gc.create_alarm (clean_weak_ref weak_ref alarm_ref)); result end module Run = struct let any_file file filename = assert_int_success (Pywrappers.pyrun_anyfileexflags (Pytypes.file_map Unix.descr_of_in_channel file) filename 1 None) let file file filename start globals locals = let fd = Pytypes.file_map Unix.descr_of_in_channel file in check_not_null (Pywrappers.pyrun_fileexflags fd filename start globals locals 1 None) let interactive_one channel name = let fd = Channel (Unix.descr_of_in_channel channel) in assert_int_success (Pywrappers.pyrun_interactiveoneflags fd name None) let interactive_loop channel name = let fd = Channel (Unix.descr_of_in_channel channel) in assert_int_success (Pywrappers.pyrun_interactiveloopflags fd name None) let simple_file channel name = let fd = Pytypes.file_map Unix.descr_of_in_channel channel in assert_int_success (Pywrappers.pyrun_simplefileexflags fd name 1 None) let simple_string string = Pywrappers.pyrun_simplestringflags string None = 0 let string s start globals locals = check_not_null (Pywrappers.pyrun_stringflags s start globals locals None) let eval ?(start = Eval) ?(globals = Module.get_dict (Module.main ())) ?(locals = globals) s = string s start globals locals let load ?(start = File) ?(globals = Module.get_dict (Module.main ())) ?(locals = globals) chan filename = file chan filename start globals locals let interactive () = interactive_loop stdin "" let frame f arg = let m = Import.add_module "_pyml" in let result = ref None in let callback = (Callable.of_function (fun _ -> result := Some (f arg); pynone ())) in Module.set m "callback" callback; ignore (eval ~start:File " from _pyml import callback callback() "); match !result with None -> failwith "frame" | Some result -> result (* let ipython () = ignore (eval ~start:File " try: from IPython import embed embed() except ImportError: from IPython.Shell import IPShellEmbed ipshell = IPShellEmbed(argv=['']) ipshell() ") *) let make_frame = frame let ipython ?(frame=true) () = let f () = let f = try Module.get_function (Import.import_module "IPython") "embed" with E _ -> let shell = Import.import_module "IPython.Shell" in let arg = [("argv", List.of_list [String.of_string ""])] in let f' = Module.get_function_with_keywords shell "IPShellEmbed" [| |] arg in Callable.to_function f' in ignore (f [| |]) in if frame then make_frame f () else f () end module Gil = struct type t = int let ensure = Pywrappers.pygilstate_ensure let release = Pywrappers.pygilstate_release let check () = Pywrappers.pygilstate_check () <> 0 let with_lock f = let t = ensure () in Fun.protect f ~finally:(fun () -> release t) end let set_argv argv = Module.set (Module.sys ()) "argv" (List.of_array_map String.of_string argv) let last_value () = Module.get (Module.builtins ()) "_" let compile ~source ~filename ?dont_inherit ?optimize mode = let mode = match mode with | `Exec -> File | `Eval -> Eval | `Single -> Single in let optimize = Stdcompat.Option.map (function | `Default -> Default | `Debug -> Debug | `Normal -> Normal | `RemoveDocstrings -> RemoveDocstrings) optimize in Module.compile ~source ~filename ?dont_inherit ?optimize mode py.mli000066400000000000000000002541061452051003100122100ustar00rootroot00000000000000(** OCaml Interface for Python. *) (** Call [initialize ()] first. *) val initialize: ?library_name:string -> ?interpreter:string -> ?version:int -> ?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 if explicitely mentioned. If [library_name] is given, it is used as the path for the library to be loaded: in this case, version parameters are ignored. If [library_name] is not given, the library is searched as described below. [version] should specify the major version number of Python (2 or 3). [minor] should specify the minor version number. If no version number is given, the version of Python is determined by the output of the shell command [python --version]. If an [interpreter] executable name is given, this executable is used in place of [python] in the previous command line. The library is searched by using [pkg-config] if available, by considering system paths, and 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 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. 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 [initialize ()] should be called afterwards. *) val on_finalize: (unit -> unit) -> unit (** [on_finalize f] registers [f ()] to be executed when [finalize] is executed. *) val is_initialized: unit -> bool (** [is_initialized ()] returns [true] if the library is initialized ([initialize ()] has been called and [finalize ()] has not been called afterwards). *) val is_debug_build: unit -> bool (** [is_debug_build ()] returns [true] if the library is a debug build. *) val get_library_filename: unit -> string option (** [get_library_filename ()] returns [Some filename] where [filename] is the path to the Python library that has been loaded, or [None] if no Python library has been loaded (for example, if the library has been statically linked with the executable). *) val version: unit -> string (** [version ()] returns the version of the Python library. E.g. ["3.5.1"]. *) val version_major: unit -> int (** [version_major ()] returns the major number (the first component) of the version of the Python library, either [2] or [3]. *) val version_minor: unit -> int (** [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. Channels suppose that the same C runtime has been used to compile both the Python library and the OCaml runtime. Warning: using channels is unsafe if runtimes differ (can lead to segmentation fault).*) type 'a file = 'a Pytypes.file = Filename of string | Channel of 'a val check_error: unit -> unit (** General functions to handle Python values *) module Object: sig type t = Pytypes.pyobject (** The type of a Python value. Structural comparison of values of type [Py.Object.t] rely on Python comparison of underlying values. That is to say, if [u] and [v] are two values of type [Py.Object.t], and by abuse of notations, if we denote also [u] and [v] their respective value in Python, we have [u = v] in OCaml if and only if [u == v] in Python, and [u < v] in OCaml if and only if [u < v] in Python, etc. Moreover, there are five values which are handled specially: - {!val:Py.null}: the value [NULL] used in the Python API for error case - {!val:Py.none}: the value [None]; - {!val:Py.Bool.t}: the value [True]; - {!val:Py.Bool.f}: the value [False]; - {!val:Py.Tuple.empty}: the value [()]. These values are guaranteed to be unique, so that the physical equality can be used to compare against their definitions: for instance, a value [v] of type [Py.Object.t] is [None] if and only if [v == Py.none]. *) val del_attr: t -> t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_DelAttr} PyObject_DelAttr} *) val del_attr_string: t -> string -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_DelAttrString} PyObject_DelAttrString} *) val del_item: t -> t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_DelItem} PyObject_DelItem} *) val del_item_string: t -> string -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_DelItemString} PyObject_DelItemString} *) val get_attr: t -> t -> t option (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_GetAttr} PyObject_GetAttr} *) val find_attr: t -> t -> t (** Equivalent to {!get_attr} but raises a [Not_found] exception in case of failure. *) val find_attr_err: t -> t -> t (** Equivalent to {!get_attr} but raises a Python exception in case of failure. *) val find_attr_opt: t -> t -> t option (** Alias for {!get_attr}. *) val get_attr_string: t -> string -> t option (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_GetAttrString} PyObject_GetAttrString} *) val find_attr_string: t -> string -> t (** Equivalent to {!get_attr_string} but raises a [Not_found] exception in case of failure. *) val find_attr_string_err: t -> string -> t (** Equivalent to {!get_attr_string} but raises a Python exception in case of failure. *) val find_attr_string_opt: t -> string -> t option (** Alias for {!get_attr_string}. *) val get_item: t -> t -> t option (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_GetItem} PyObject_GetItem} *) val find: t -> t -> t (** Equivalent to {!get_item} but raises a [Not_found] exception in case of failure. *) val find_err: t -> t -> t (** Equivalent to {!get_item} but raises a Python exception in case of failure. *) val find_opt: t -> t -> t option (** Alias for {!get_item}. *) val get_item_string: t -> string -> t option (** [get_item_string o key] returns the element corresponding to the object [key] or [None] on failure. *) val find_string: t -> string -> t (** Equivalent to {!get_item_string} but raises a [Not_found] exception in case of failure. *) val find_string_err: t -> string -> t (** Equivalent to {!get_item_string} but raises a Python exception in case of failure. *) val find_string_opt: t -> string -> t option (** Alias for {!get_item_string}. *) val get_iter: t -> t (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_GetIter} PyObject_GetIter} *) val get_type: t -> t (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_GetType} PyObject_GetType} *) val has_attr: t -> t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_HasAttr} PyObject_HasAttr} *) val has_attr_string: t -> string -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_HasAttrString} PyObject_HasAttrString} *) val hash: t -> int64 (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_Hash} PyObject_Hash} *) val is_true: t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_IsTrue} PyObject_IsTrue} *) val not: t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_Not} PyObject_Not} *) val is_instance: t -> t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_IsInstance} PyObject_IsInstance} *) val is_subclass: t -> t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_IsSubclass} PyObject_IsSubclass} *) val print: t -> out_channel file -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_Print} PyObject_Print} *) val repr: t -> t (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_Repr} PyObject_Repr} *) val rich_compare: t -> t -> compare -> t (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_RichCompare} PyObject_RichCompare} *) val rich_compare_bool: t -> t -> compare -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_RichCompareBool} PyObject_RichCompareBool} *) val set_attr: t -> t -> t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_SetAttr} PyObject_SetAttr} *) val set_attr_string: t -> string -> t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_SetAttrString} PyObject_SetAttrString} *) val set_item: t -> t -> t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_SetItem} PyObject_SetItem} *) val set_item_string: t -> string -> t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_SetItemString} PyObject_SetItemString} *) val str: t -> t (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_Str} PyObject_Str} *) val string_of_repr: t -> string (** [string_of_repr o] returns the string [repr o]. We have [Py.Object.to_string o = Py.String.to_string (Py.Object.repr o)]. *) val to_string: t -> string (** [to_string o] returns the string [str o]. We have [Py.Object.to_string o = Py.String.to_string (Py.Object.str o)]. *) val as_char_buffer: t -> string (** Wrapper for {{:https://docs.python.org/3/c-api/objbuffer.html#c.PyObject_AsCharBuffer} PyObject_AsCharBuffer} *) val as_read_buffer: t -> string (** Wrapper for {{:https://docs.python.org/3/c-api/objbuffer.html#c.PyObject_AsReadBuffer} PyObject_AsReadBuffer} *) val as_write_buffer: t -> string (** Wrapper for {{:https://docs.python.org/3/c-api/objbuffer.html#c.PyObject_AsWriteBuffer} PyObject_AsWriteBuffer} *) val reference_count: t -> int (** [reference_count o] returns the number of references to the Python object [o]. *) val format: Format.formatter -> t -> unit (** [Py.Object.format fmt v] is equivalent to [Format.pp_print_string fmt (Py.Object.to_string v)]. Can be used as printer for the top-level: [#install_printer Py.Object.format]. *) val format_repr: Format.formatter -> t -> unit (** [Py.Object.format_repr fmt v] is equivalent to [Format.pp_print_string fmt (Py.Object.string_of_repr v)]. Can be used as printer for the top-level: [#install_printer Py.Object.format_repr]. *) val call_function_obj_args: t -> t array -> t (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_CallFunctionObjArgs} PyObject_CallFunctionObjArgs} *) val call_method_obj_args: t -> t -> t array -> t (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_CallMethodObjArgs} PyObject_CallMethodObjArgs} *) val call_method: t -> string -> t array -> t (** [Py.Object.call_method o m args] is equivalent to [Py.Object.call_method_obj_args o (Py.String.of_string m) args]. *) val call: t -> t -> t -> t (** Wrapper for {{:https://docs.python.org/3/c-api/object.html#c.PyObject_Call} PyObject_Call} *) 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 (** [E (errtype, errvalue)] is a Python error. [errtype] is the type of the exception. [errvalue] is the value. *) val null: Object.t (** The value [NULL] of the C Python API. [null] is useful for calling directly the functions of {!Pywrappers} module. The value should not appear when using the functions of the [Py] module. This value is guaranteed to be the unique value associated to [NULL]. *) val is_null: Object.t -> bool (** [Py.is_null v] is true if and only if [v] is [NULL]. Since [Py.none] is guaranteed to be the unique value associated to [NULL], [Py.is_null v] is equivalent to [v == Py.null]. *) val check_not_null: Object.t -> Object.t (** [check_not_null v] checks that [v] is not [null] and returns [v]. Raises the current Python error as exception otherwise. *) val none: Object.t (** The value [None] of Python. This value is guaranteed to be the unique value associated to [None]. *) val is_none: Object.t -> bool (** [Py.is_none v] is true if and only if [v] is [None]. Since [Py.none] is guaranteed to be the unique value associated to [None], [Py.is_none v] is equivalent to [v == Py.none]. *) val set_program_name: string -> unit (** Sets the program name (by default, [Sys.argv.(0)]). The function can be called before [initialize ()] and the value is preserved from one initialization to the other. *) val set_python_home: string -> unit (** Sets the path of the Python home. The function can be called before [initialize ()] and the value is preserved from one initialization to the other. *) val add_python_path: string -> unit (** Adds a path to Python search path. The function can be called before [initialize ()] and the value is preserved from one initialization to the other. *) val get_program_name: unit -> string (** Gets the program name (by default, [Sys.argv.(0)]). The function can be called before [initialize ()]. *) val get_python_home: unit -> string (** Gets the path of the Python home. The function can be called before [initialize ()]. *) val get_program_full_path: unit -> string (** Wrapper for {{: https://docs.python.org/3/c-api/init.html#Py_GetProgramFullPath} Py_GetProgramFullPath}. *) val get_prefix: unit -> string (** Wrapper for {{: https://docs.python.org/3/c-api/init.html#Py_GetPrefix} Py_GetPrefix}. *) val get_exec_prefix: unit -> string (** Wrapper for {{: https://docs.python.org/3/c-api/init.html#Py_GetExecPrefix} Py_GetExecPrefix}. *) val get_path: unit -> string (** Wrapper for {{: https://docs.python.org/3/c-api/init.html#Py_GetPath} Py_GetPath}. *) val get_version: unit -> string (** Wrapper for {{: https://docs.python.org/3/c-api/init.html#Py_GetVersion} Py_GetVersion}. *) val get_platform: unit -> string (** Wrapper for {{: https://docs.python.org/3/c-api/init.html#Py_GetPlatform} Py_GetPlatform}. *) val get_copyright: unit -> string (** Wrapper for {{: https://docs.python.org/3/c-api/init.html#Py_GetCopyright} Py_GetCopyright}. *) val get_compiler: unit -> string (** Wrapper for {{: https://docs.python.org/3/c-api/init.html#Py_GetCompiler} Py_GetCompiler}. *) val get_build_info: unit -> string (** Wrapper for {{: https://docs.python.org/3/c-api/init.html#Py_GetBuildInfo} Py_GetBuildInfo}. *) (** Interface for Python values of type [Bool]. *) module Bool: sig val t: Object.t (** The Python value [True]. This value is guaranteed to be the unique value associated to [True]. *) val is_true: Object.t -> bool (** [Py.is_true v] is true if and only if [v] is [True]. Since [Py.Bool.t] is guaranteed to be the unique value associated to [True], [Py.is_true v] is equivalent to [v == Py.t]. *) val f: Object.t (** The Python value [False]. This value is guaranteed to be the unique value associated to [False]. *) val is_false: Object.t -> bool (** [Py.is_false v] is true if and only if [v] is [False]. Since [Py.Bool.f] is guaranteed to be the unique value associated to [False], [Py.is_false f] is equivalent to [v == Py.f]. *) val check: Object.t -> bool (** [check v] returns [true] if [v = t] or [v = f]. *) val of_bool: bool -> Object.t (** [of_bool b] returns [t] if [b = true], and [f] if [b = false]. *) val to_bool: Object.t -> bool (** [to_bool b] returns [true] if [b = t], and [false] if [b = f]. [Failure] is raised if [b] is neither [t] nor [f]. *) end (** Interface for Python values of type [Callable]. *) module Callable: sig val check: Object.t -> bool (** [check v] returns [true] if [v] is callable. 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 function [f]. Arguments are passed as a tuple. If [f] 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). If [f] raises any other exception, this exception bypasses the Python interpreter. *) val of_function_as_tuple_and_dict: ?name:string -> ?docstring:string -> (Object.t -> Object.t -> Object.t) -> Object.t (** [of_function_as_tuple_and_dict f] returns a Python callable object that calls the function [f]. Arguments are passed as a tuple and a dictionary of keywords. *) val of_function: ?name:string -> ?docstring:string -> (Object.t array -> Object.t) -> Object.t (** Equivalent to {!of_function_as_tuple} but with an array of Python objects instead of a tuple for passing arguments. *) val of_function_with_keywords: ?name:string -> ?docstring:string -> (Object.t array -> Object.t -> Object.t) -> Object.t (** Equivalent to {!of_function_as_tuple_and_dict} but with an array of Python objects instead of a tuple for passing arguments. The dictionary of keywords is passed as such as it is more efficient to access arguments with ``Py.Dict.find_string``, rather than using ``List.assoc`` with an associative list. *) val to_function_as_tuple: Object.t -> Object.t -> Object.t (** [to_function_as_tuple c] returns a function [f] such that [f args] calls the Python callable [c] with the Python tuple [args] as arguments. *) val to_function_as_tuple_and_dict: Object.t -> Object.t -> Object.t -> Object.t (** [to_function_as_tuple_and_dict c] returns a function [f] such that [f args dict] calls the Python callable [c] with the Python tuple [args] and the dictionary of keywords [dict] as arguments. *) val to_function: Object.t -> Object.t array -> Object.t (** Equivalent to {!to_function_as_tuple} but with an array of Python objects instead of a tuple for passing arguments. *) val to_function_with_keywords: Object.t -> Object.t array -> (string * Object.t) list -> Object.t (** Equivalent to {!to_function_as_tuple_and_dict} but with an array of Python objects instead of a tuple and an associative list instead of a dictionary for passing arguments. *) end (** 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 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], such that [unwrap (wrap v) = v]. [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]. *) val is_valid: Object.t -> string -> bool (** Wrapper for {{: https://docs.python.org/3/c-api/capsule.html#c.PyCapsule_IsValid} PyCapsule_IsValid}. OCaml capsules have the name ["ocaml-capsule"]. We have [check v = is_valid v "ocaml-capsule"]. *) val unsafe_wrap_value: 'a -> Object.t (** [unsafe_wrap_value v] transforms the value [v] to an opaque Python object. *) val unsafe_unwrap_value: Object.t -> 'a (** [unsafe_unwrap_value v] transforms the opaque Python object [w] previously obtained with [unsafe_wrap_value v] into the original OCaml value [v]. *) end (** Defining a new class type *) module Class: sig val init: ?parents:(Object.t list) -> ?fields:((string * Object.t) list) -> ?methods:((string * Object.t) list) -> string -> Object.t (** [init ~parents ~fields ~methods classname] Returns a new class type. @param parents list of base classes (default: [[]]). @param fields associative list for field values (default : [[]]). @param methods associative list for method closures (default : [[]]). *) end (** Interface for Python values of type [Long]. *) module Long: sig val check: Object.t -> bool (** [check o] returns [true] if [o] is a Python long. *) val of_int64: int64 -> Object.t (** [of_int i] returns the Python long with the value [i]. Wrapper for {{: https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong} PyLong_FromLong}. *) val to_int64: Object.t -> int64 (** [to_int o] takes a Python long [o] as arguments and returns the corresponding 64-bit integer value. A Python exception ([Py.E _]) is raised if [o] is not a long. Wrapper for {{: https://docs.python.org/3/c-api/long.html#c.PyLong_AsLong} PyLong_AsLong}. *) val of_int: int -> Object.t (** [of_int i] returns the Python long with the value [i]. We have [of_int i = of_int64 (Int64.of_int i)]. *) val to_int: Object.t -> int (** [to_int o] takes a Python long [o] as arguments and returns the corresponding integer value. A Python exception ([Py.E _]) is raised if [o] is not a long. We have [to_int o = Int64.to_int (to_int 64 o)]. *) val from_string: string -> int -> Object.t * int (** [from_string s base] parses [s] as a number written in [base] and returns [(o, l)] where [o] is the Python long which has been read, and [l] is the number of characters that has been parsed. Wrapper for {{: https://docs.python.org/3/c-api/long.html#c.PyLong_FromString} PyLong_FromString}. *) val of_string: ?base:int -> string -> Object.t (** [of_string ?base s] parses [s] and returns the Python long that has been read. By default, [base] is [0]: the radix is determined based on the leading characters of [s]. *) val to_string: Object.t -> string (** Synonym for [Py.Object.to_string]. *) end (** Interface for Python values of type [Int] if Python 2, [Long] if Python 3. *) module Int: sig val check: Object.t -> bool (** [check o] returns [true] if [o] is a Python int. *) val of_int64: int64 -> Object.t (** [of_int i] returns the Python int with the value [i]. Wrapper for {{: https://docs.python.org/2/c-api/int.html#c.PyInt_FromLong} PyInt_FromLong}. *) val to_int64: Object.t -> int64 (** [to_int o] takes a Python int [o] as arguments and returns the corresponding 64-bit integer value. A Python exception ([Py.E _]) is raised if [o] is not a long. Wrapper for {{: https://docs.python.org/2/c-api/int.html#c.PyInt_AsLong} PyInt_AsLong}. *) val of_int: int -> Object.t (** [of_int i] returns the Python int with the value [i]. We have [of_int i = of_int64 (Int64.of_int i)]. *) val to_int: Object.t -> int (** [to_int o] takes a Python int [o] as arguments and returns the corresponding integer value. A Python exception ([Py.E _]) is raised if [o] is not a long. We have [to_int o = Int64.to_int (to_int 64 o)]. *) val of_string: ?base:int -> string -> Object.t (** Synonym for [Py.Long.of_string]. *) val to_string: Object.t -> string (** Synonym for [Py.Long.to_string]. *) end (** Interface for Python values of type [Dict]. *) module Dict: sig val check: Object.t -> bool (** [check o] returns [true] if [o] is a Python dictionary. *) val clear: Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_Clear} PyDict_Clear} *) val copy: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_Copy} PyDict_Copy} *) val create: unit -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_New} PyDict_New} *) val del_item: Object.t -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_DelItem} PyDict_DelItem} *) val del_item_string: Object.t -> string -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_DelItemString} PyDict_DelItemString} *) val get_item: Object.t -> Object.t -> Object.t option (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_GetItem} PyDict_GetItem} *) val find: Object.t -> Object.t -> Object.t (** [find p key] returns the object from Python dictionary [p] which has a key [key]. Equivalent to {!get_item} but [find] raises [Not_found] if the key [key] is not present. *) val find_opt: Object.t -> Object.t -> Object.t option (** Alias for {!get_item}. *) val get_item_string: Object.t -> string -> Object.t option (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_GetItemString} PyDict_GetItemString} *) val find_string: Object.t -> string -> Object.t (** [find_string p key] returns the object from Python dictionary [p] which has a key [key]. Equivalent to {!get_item_string} but [find_string] raises [Not_found] if the key [key] is not present. *) val find_string_opt: Object.t -> string -> Object.t option (** Alias for {!get_item_string}. *) val keys: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_Keys} PyDict_Keys} *) val items: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_Items} PyDict_Items} *) val set_item: Object.t -> Object.t -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItem} PyDict_SetItem} *) val set_item_string: Object.t -> string -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_SetItemString} PyDict_SetItemString} *) val size: Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_Size} PyDict_Size} *) val values: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/dict.html#c.PyDict_Clear} PyDict_Str} *) val iter: (Object.t -> Object.t -> unit) -> Object.t -> unit (** [iter f dict] applies [f key value] for each pair [(key, value)] in the Python dictionary [dict]. *) val fold: (Object.t -> Object.t -> 'a -> 'a) -> Object.t -> 'a -> 'a (** [fold f dict v] returns [f key1 value1 (... (f keyn valuen dict))] where [(key1, value1)], ..., [(keyn, valuen)] are the bindings of the Python dictionary [dict]. *) val for_all: (Object.t -> Object.t -> bool) -> Object.t -> bool (** [for_all p dict] checks whether all the bindings [(key, value)] of the Python dictionary [dict] satisfy the predicate [p key value]. *) val exists: (Object.t -> Object.t -> bool) -> Object.t -> bool (** [for_all p dict] checks that there is at least one binding [(key, value)] among those of the Python dictionary [dict] that satisfies the predicate [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]. *) val to_bindings_map: (Object.t -> 'a) -> (Object.t -> 'b) -> Object.t -> ('a * 'b) list (** [to_bindings_map fkey fvalue o] returns all the pairs [(fkey key, fvalue value)] in the Python dictionary [o]. *) val to_bindings_string: Object.t -> (string * Object.t) list (** [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]. *) val of_bindings_map: ('a -> Object.t) -> ('b -> Object.t) -> ('a * 'b) list -> Object.t (** [of_bindings_map fkey fvalue b] returns then Python dictionary mapping all the pairs [(fkey key, fvalue value)] in [b]. *) val of_bindings_string: (string * Object.t) list -> Object.t (** [of_bindings_string b] returns then Python dictionary mapping all the pairs [(key, value)] in [b]. *) val singleton: Object.t -> Object.t -> Object.t (** [singleton key value] returns the one-element Python dictionary that maps [key] to [value] *) val singleton_string: string -> Object.t -> Object.t (** [singleton key value] returns the one-element Python dictionary that maps [key] to [value] *) end (** Interface for Python values of type [Set]. *) module Set: sig val check: Object.t -> bool (** [check o] returns [true] if [o] is a Python set. *) val add: Object.t -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/set.html#c.PySet_Add} PySet_Add} *) val clear: Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/set.html#c.PySet_Clear} PySet_Clear} *) val contains: Object.t -> Object.t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/set.html#c.PySet_Contains} PySet_Contains} *) val copy: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/set.html#c.PySet_New} PySet_New} *) val create: unit -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/set.html#c.PySet_New} PySet_New} *) val discard: Object.t -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/set.html#c.PySet_Discard} PySet_Discard} *) val size: Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/set.html#c.PySet_Size} PySet_Size} *) val to_list: Object.t -> Object.t list (** [to_list o] returns the list of all elements in Python set [o]. *) val to_list_map: (Object.t -> 'a) -> Object.t -> 'a list (** [to_list_map f o] returns the list of [f v] for all elements v in Python set [o]. *) val of_list: Object.t list -> Object.t (** [of_list l] returns then Python set containing all elements from [l]. *) val of_list_map: ('a -> Object.t) -> 'a list -> Object.t (** [of_list_map f l] returns then Python set containing [f e] for any [e] from [l]. *) end module Err: sig type t = Exception | StandardError | ArithmeticError | LookupError | AssertionError | AttributeError | EOFError | EnvironmentError | FloatingPointError | IOError | ImportError | IndexError | KeyError | KeyboardInterrupt | MemoryError | NameError | NotImplementedError | OSError | OverflowError | ReferenceError | RuntimeError | SyntaxError | SystemExit | TypeError | ValueError | ZeroDivisionError | StopIteration val clear: unit -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Clear} PyErr_Clear} *) val exception_matches: Object.t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_ExceptionMatches} PyErr_ExceptionMatches} *) val fetch: unit -> (Object.t * Object.t * Object.t) option (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Fetch} PyErr_Fetch}. *) val fetched: unit -> (Object.t * Object.t * Object.t) option (** Exception fetched when {!Py.E} has been raised. *) val given_exception_matches: Object.t -> Object.t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_GivenExceptionMatches} PyErr_GivenExceptionMatches} *) val occurred: unit -> Object.t option (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Occurred} PyErr_Occurred} *) val print: unit -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Print} PyErr_Print} *) val print_ex: int -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_PrintEx} PyErr_PrintEx} *) val restore: Object.t -> Object.t -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Restore} PyErr_Restore} *) val restore_tuple: Object.t * Object.t * Object.t -> unit (** [restore_tuple (ptype, pvalue, ptraceback)] is equivalent to [Py.Err.restore ptype pvalue ptraceback]. *) val restore_fetch: unit -> unit (** Restore the exception returned by [Py.Err.fetch ()] and raise [Failure] if [None]. *) val restore_fetched: unit -> unit (** Restore the exception returned by [Py.Err.fetched ()] and raise [Failure] if [None]. *) val set_error: t -> string -> unit (** [set_error e msg] calls [Py.Err.set_string e msg] with a predefined error type. In a closure/method/callback, it is recommended to raise a [Py.Err _] exception instead. *) val set_none: Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetNone} PyErr_SetNone} *) val set_string: Object.t -> string -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/exceptions.html#c.PyErr_SetString} PyErr_SetString} *) val set_object: Object.t -> Object.t -> unit (** Wrapper for {{: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 type frame = { filename : string ; function_name : string ; line_number : int } val create_frame : frame -> Object.t type t = frame list end exception Err of Err.t * string (** Represents an exception to be set with {!Err.set_error} in a callback. *) exception Err_with_traceback of Err.t * string * Traceback.t (** Represents an exception with traceback information to be set with {!Err.restore}. *) module Eval: sig val call_object: Object.t -> Object.t -> Object.t (** See {{:https://docs.python.org/3.0/extending/extending.html} Extending Python with C or C++} *) val call_object_with_keywords: Object.t -> Object.t -> Object.t -> Object.t (** See {{:https://docs.python.org/3.0/extending/extending.html} Extending Python with C or C++} *) val get_builtins: unit -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/reflection.html#c.PyEval_GetBuiltins} PyEval_GetBuiltins} *) val get_globals: unit -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/reflection.html#c.PyEval_GetGlobals} PyEval_GetGlobals} *) val get_locals: unit -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/reflection.html#c.PyEval_GetLocals} PyEval_GetLocals} *) end (** Interface for Python values of type [Float]. *) module Float: sig val check: Object.t -> bool (** [check o] returns [true] if [o] is a Python float. *) val of_float: float -> Object.t (** [of_float f] returns the Python long with the value [f]. Wrapper for {{:https://docs.python.org/3/c-api/float.html#c.PyFloat_AsDouble} PyFloat_AsDouble}. *) val to_float: Object.t -> float (** [to_float o] returns the floating-point vale stored in [o]. A Python exception ([Py.E _]) is raised if [o] is not a float. Wrapper for {{: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 (* This function has been removed from Python 3.9, and was marked "for internal use only" before. val cleanup: unit -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/import.html#c.PyImport_Cleanup} PyImport_Cleanup} *) *) val add_module: string -> Object.t (** Wrapper for {{: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.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} *) val exec_code_module_ex: string -> Object.t -> string -> Object.t (** 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} *) val get_module_dict: unit -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/import.html#c.PyImport_GetModuleDict} PyImport_GetModuleDict} *) val import_frozen_module: string -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/import.html#c.PyImport_ImportFrozenModule} PyImport_ImportFrozenModule} *) val import_module: string -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/import.html#c.PyImport_ImportModule} PyImport_ImportModule} Note that Python memoizes imported module, so that you will get the same object if you import the same module twice. ({{:https://github.com/thierry-martinez/pyml/issues/16}GitHub issue #16}) {[let m = Py.Import.import_module "json" and m' = Py.Import.import_module "json" in assert (m = m')]} *) val import_module_opt: string -> Object.t option (** [import_module_opt m] imports the module [m] and returns the module object if the import succeeds:. in this case, it is equivalent to [Some (import_module m)]. If the module is not found, i.e. if [import_module] raises a Python exception of class [ModuleNotFoundError], then [try_import_module] returns [None]. *) val try_import_module: string -> Object.t option (** Alias for {!import_module_opt}. *) val import_module_ex: string -> Object.t -> Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/import.html#c.PyImport_ImportModuleEx} PyImport_ImportModuleEx} *) val import_module_level: string -> Object.t -> Object.t -> Object.t -> int -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/import.html#c.PyImport_ImportModuleLevel} PyImport_ImportModuleLevel} *) val reload_module: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/import.html#c.PyImport_ReloadModule} PyImport_ReloadModule} *) end val import: string -> Object.t (** Equivalent to {!Import.import_module}. *) val import_opt: string -> Object.t option (** Equivalent to {!Import.import_module_opt}. *) (** Interface for Python values of type [Iter]. *) module Iter: sig val check: Object.t -> bool (** [check o] returns [true] if [o] is an iterator. *) val next: Object.t -> Object.t option (** [next i] returns the next value from the iteration [i]. If there are no remaining values, returns [None]. Wrapper for {{:https://docs.python.org/3/c-api/iter.html#c.PyIter_Next} PyIter_Next}. *) val iter: (Object.t -> unit) -> Object.t -> unit (** [iter f i] iteratively calls [f v] with all the remaining values of the iteration [i]. *) val to_list: Object.t -> Object.t list (** [to_list i] returns the list of all the remaining values from the iteration [i]. *) val to_list_map: (Object.t -> 'a) -> Object.t -> 'a list (** [to_list_map f i] returns the list of the results of [f] applied to all the remaining values from the iteration [i]. [to_list_map f s] is equivalent to [List.map f (to_list s)] but is tail-recursive and [f] is applied to the elements of [s] in the reverse order. *) val of_seq: Object.t Stdcompat.Seq.t -> Object.t (** [of_seq s] returns an interator that iterates over the values of the sequence [s]. *) val of_seq_map: ('a -> Object.t) -> 'a Stdcompat.Seq.t -> Object.t (** [of_seq_map f s] returns an interator that iterates over the results of [f] applied to the values of the sequence [s]. [Py.Iter.of_seq_map f s] is equivalent to [Py.Iter.of_seq (Seq.map f s)]. *) val to_seq: Object.t -> Object.t Stdcompat.Seq.t (** [to_seq i] returns the sequence of the values from the iteration [i]. The Python iteration is consumed while the sequence is browsed. Values are memoized, so that the sequence can be browsed many times. *) val to_seq_map: (Object.t -> 'a) -> Object.t -> 'a Stdcompat.Seq.t (** [to_seq_map f i] returns the sequence of the results of [f] applied to the values from the iteration [i]. The Python iteration is consumed while the sequence is browsed. Values are memoized, so that the sequence can be browsed many times. *) val unsafe_to_seq: Object.t -> Object.t Stdcompat.Seq.t (** [unsafe_to_seq i] returns the sequence of the values from the iteration [i]. The Python iteration is consumed while the sequence is browsed. Warning: values are not memoized, so that the sequence can be browsed only once. *) val unsafe_to_seq_map: (Object.t -> 'a) -> Object.t -> 'a Stdcompat.Seq.t (** [unsafe_to_seq_map f i] returns the sequence of the results of [f] applied to the values from the iteration [i]. The Python iteration is consumed while the sequence is browsed. Warning: values are not memoized, so that the sequence can be browsed only once. *) val of_list: Object.t list -> Object.t (** [of_list l] returns an interator that iterates over the values of the list [l]. *) val of_list_map: ('a -> Object.t) -> 'a list -> Object.t (** [of_list_map f l] returns an interator that iterates over the results of [f] applied to the values of the list [l]. [Py.Iter.of_list_map f s] is equivalent to [Py.Iter.of_list (List.map f s)] but is tail-recursive. *) val fold_left: ('a -> Object.t -> 'a) -> 'a -> Object.t -> 'a (** [fold_left f v i] returns [(f (...(f v i1)...) in)] where [i1], ..., [in] are the remaining values from the iteration [i]. *) val fold_right: (Object.t -> 'a -> 'a) -> Object.t -> 'a -> 'a (** [fold_right f i v] returns [(f i1 (...(f v in)...)] where [i1], ..., [in] are the remaining values from the iteration [i]. This function is not tail-recursive. *) val for_all: (Object.t -> bool) -> Object.t -> bool (** [for_all p i] checks if [p] holds for all the remaining values from the iteration [i]. *) val exists: (Object.t -> bool) -> Object.t -> bool (** [exists p i] checks if [p] holds for at least one of the remaining values from the iteration [i]. *) val create: (unit -> Object.t option) -> Object.t (** [create next] returns an iterator that calls [next]. *) val seq_iter: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/iterator.html#c.PySeqIter_New} PySeqIter_New} *) val call_iter: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/iterator.html#c.PyCallIter_New} PyCallIter_New} *) val create_call: (unit -> Object.t option) -> Object.t (** [create_call next] returns an iterator that calls [next]. The difference with [create] is that this uses [PyCallIter_New] rather than creating an object and use the __next__ method. *) end (** Interface for Python values of type [List]. *) module List: sig val check: Object.t -> bool (** [check v] returns [true] if [v] is a Python list. *) val create: int -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/list.html#c.PyList_New} PyList_New} *) val get_item: Object.t -> int -> Object.t (** Equivalent to {!Sequence.get_item}. *) val get: Object.t -> int -> Object.t (** Equivalent to {!get_item}. *) val set_item: Object.t -> int -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/list.html#c.PyList_SetItem} PyList_SetItem} *) val set: Object.t -> int -> Object.t -> unit (** Equivalent to {!set_item}. *) val size: Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/list.html#c.PyList_Size} PyList_Size} *) val length: Object.t -> int (** Equivalent to {!size}. *) val init: int -> (int -> Object.t) -> Object.t (** [init n f] returns the Python list [[f 0, f 1, ..., f (n - 1)]]. *) val of_array: Object.t array -> Object.t (** [of_array a] returns the Python list with the same elements as [a]. *) val of_array_map: ('a -> Object.t) -> 'a array -> Object.t (** [of_array_map f a] returns the Python list [(f a0, ..., f ak)] where [a0], ..., [ak] are the elements of [a]. *) val to_array: Object.t -> Object.t array (** Equivalent to {!Sequence.to_array}. *) val to_array_map: (Object.t -> 'a) -> Object.t -> 'a array (** Equivalent to {!Sequence.to_array_map}. *) val of_list: Object.t list -> Object.t (** [of_list l] returns the Python list with the same elements as [l]. *) val of_list_map: ('a -> Object.t) -> 'a list -> Object.t (** [of_list f l] returns the Python list [(f l1, ..., f ln)] where [l1], ..., [ln] are the elements of [l]. [of_list_map f l] is equivalent to [of_list (List.map f l)] but is tail-recursive and [f] is applied to the elements of [l] in the reverse order. *) val to_list: Object.t -> Object.t list (** Equivalent to {!Sequence.to_list}. *) val to_list_map: (Object.t -> 'a) -> Object.t -> 'a list (** Equivalent to {!Sequence.to_list_map}. *) val fold_left: ('a -> Object.t -> 'a) -> 'a -> Object.t -> 'a (** Equivalent to {!Sequence.fold_left}. *) val fold_right: (Object.t -> 'a -> 'a) -> Object.t -> 'a -> 'a (** Equivalent to {!Sequence.fold_right}. *) val for_all: (Object.t -> bool) -> Object.t -> bool (** Equivalent to {!Sequence.for_all}. *) val exists: (Object.t -> bool) -> Object.t -> bool (** Equivalent to {!Sequence.exists}. *) val of_sequence: Object.t -> Object.t (** Equivalent to {!Sequence.list}. *) val of_seq: Object.t Stdcompat.Seq.t -> Object.t (** [of_seq s] returns the Python list with the same elements as [s]. *) val to_seq: Object.t -> Object.t Stdcompat.Seq.t (** Equivalent to {!Sequence.to_seq}. *) val to_seqi: Object.t -> (int * Object.t) Stdcompat.Seq.t (** Equivalent to {!Sequence.to_seqi}. *) val singleton: Object.t -> Object.t (** [singleton o] returns the Python list [[o]]. *) end (** Interface for Python values with a [Mapping] interface. *) module Mapping: sig val check: Object.t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/mapping.html#c.PyMapping_Check} PyMapping_Check} *) val get_item_string: Object.t -> string -> Object.t option (** Wrapper for {{:https://docs.python.org/3/c-api/mapping.html#c.PyMapping_GetItemString} PyMapping_GetItemString} *) val find_string: Object.t -> string -> Object.t (** Equivalent to {!get_item_string} but raises a [Not_found] exception in case of failure. *) val find_string_opt: Object.t -> string -> Object.t option (** Alias for {!get_item_string}. *) val has_key: Object.t -> Object.t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/mapping.html#c.PyMapping_HasKey} PyMapping_HasKey} *) val has_key_string: Object.t -> string -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/mapping.html#c.PyMapping_HasKeyString} PyMapping_HasKeyString} *) val length: Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/mapping.html#c.PyMapping_Length} PyMapping_Length} *) val set_item_string: Object.t -> string -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/mapping.html#c.PyMapping_SetItemString} PyMapping_SetItemString} *) val size: Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/mapping.html#c.PyMapping_Size} PyMapping_Size} *) end (** Interface for Python values of type [Method]. *) module Method: sig val create: Object.t -> Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/method.html#c.PyMethod_New} PyMethod_New} *) val get_function: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/method.html#c.PyMethod_GetFunction} PyMethod_GetFunction} *) val self: Object.t -> Object.t option (** Wrapper for {{: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 (** [check o] returns [true] if [o] is a Python module. *) val create: string -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/module.html#c.PyModule_New} PyModule_New} *) val get_dict: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/module.html#c.PyModule_GetDict} PyModule_GetDict} *) val get_filename: Object.t -> string (** Wrapper for {{:https://docs.python.org/3/c-api/module.html#c.PyModule_GetFilename} PyModule_GetFilename} *) val get_name: Object.t -> string (** Wrapper for {{:https://docs.python.org/3/c-api/module.html#c.PyModule_GetName} PyModule_GetName} *) val get: Object.t -> string -> Object.t (** Equivalent to {!Object.find_attr_string_err}. *) val get_opt: Object.t -> string -> Object.t option (** Equivalent to {!Object.find_attr_string_opt}. *) val get_function: Object.t -> string -> Object.t array -> Object.t (** [Py.Module.get_function m name] is equivalent to [Py.Callable.to_function (Py.Module.get m name)]. *) val get_function_opt: Object.t -> string -> (Object.t array -> Object.t) option (** [Py.Module.get_function_opt] is equivalent to [Py.Module.get_function] but returns [None] in case of failure. *) val get_function_with_keywords: Object.t -> string -> Object.t array -> (string * Object.t) list -> Object.t (** [Py.Module.get_function_with_keywords m name] is equivalent to [Py.Callable.to_function_with_keywords (Py.Module.get m name)]. *) val get_function_with_keywords_opt: Object.t -> string -> (Object.t array -> (string * Object.t) list -> Object.t) option (** [Py.Module.get_function_with_keywords_opt] is equivalent to [Py.Module.get_function_with_keywords] but returns [None] in case of failure. *) val set: Object.t -> string -> Object.t -> unit (** Equivalent to {!Object.set_attr_string}. *) val set_function: Object.t -> string -> (Object.t array -> Object.t) -> unit (** [Py.Module.set_function m name f] is equivalent to [Py.Module.set m name (Py.Callable.of_function f)]. *) val set_function_with_keywords: Object.t -> string -> (Object.t array -> Object.t -> Object.t) -> unit (** [Py.Module.set_function_with_keywords m name f] is equivalent to [Py.Module.set m name (Py.Callable.of_function_with_keywords f)]. *) val remove: Object.t -> string -> unit (** Equivalent to {!Object.del_attr_string}. *) val main: unit -> Object.t (** Returns the [__main__] module. We have [Py.Module.main () = Py.Module.add_module "__main__"]. *) val sys: unit -> Object.t (** Returns the [sys] module. We have [Py.Module.sys () = Py.Module.import_module "sys"]. *) val builtins: unit -> Object.t (** Returns the [__builtins__] module. We have [Py.Module.builtins () = Py.Module.find (Py.Module.main ()) "__builtins__"]. *) 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]. *) module Number: sig val absolute: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Absolute} PyNumber_Absolute} *) val add: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Add} PyNumber_Add} *) val number_and: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_And} PyNumber_And} *) val divmod: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Divmod} PyNumber_Divmod} *) val float: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Float} PyNumber_Float} *) val floor_divide: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_FloorDivide} PyNumber_FloorDivide} *) val in_place_add: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceAdd} PyNumber_InPlaceAdd} *) val in_place_and: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceAnd} PyNumber_InPlaceAnd} *) val in_place_floor_divide: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceFloorDivide} PyNumber_InPlaceFloorDivide} *) val in_place_lshift: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceLshift} PyNumber_InPlaceLshift} *) val in_place_multiply: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceMultiply} PyNumber_InPlaceMultiply} *) val in_place_or: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceOr} PyNumber_InPlaceOr} *) val in_place_power: ?modulo:Object.t -> Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlacePower} PyNumber_InPlacePower} *) val in_place_remainder: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceRemainder} PyNumber_InPlaceRemainder} *) val in_place_rshift: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceRshift} PyNumber_InPlaceRshift} *) val in_place_subtract: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceSubtract} PyNumber_InPlaceSubtract} *) val in_place_true_divide: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceTrueDivide} PyNumber_InPlaceTrueDivide} *) val in_place_xor: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_InPlaceXor} PyNumber_InPlaceXor} *) val invert: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Invert} PyNumber_Invert} *) val lshift: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Lshift} PyNumber_Lshift} *) val multiply: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Multiply} PyNumber_Multiply} *) val negative: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Negative} PyNumber_Negative} *) val number_or: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Or} PyNumber_Or} *) val positive: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Positive} PyNumber_Positive} *) val power: ?modulo:Object.t -> Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Power} PyNumber_Power} *) val remainder: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Remainder} PyNumber_Remainder} *) val rshift: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Rshift} PyNumber_Rshift} *) val subtract: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Subtract} PyNumber_Subtract} *) val true_divide: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_TrueDivide} PyNumber_TrueDivide} *) val number_xor: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/number.html#c.PyNumber_Xor} PyNumber_Xor} *) val check: Object.t -> bool (** [check v] returns [true] if [v] is a Python float or a Python integer/long. *) val to_float: Object.t -> float (** [to_float v] returns the floating-point value equal to the Python integer or Python float [v]. Raises a failure ([Failure _]) if [v] is neither a float nor an integer. *) val of_int: int -> Object.t (** Synonym of {!Py.Int.of_int} *) val of_int64: int64 -> Object.t (** Synonym of {!Py.Int.of_int64} *) val of_float: float -> Object.t (** Synonym of {!Py.Float.of_float} *) val ( + ): Object.t -> Object.t -> Object.t (** Synomym of {!add} *) val ( - ): Object.t -> Object.t -> Object.t (** Synomym of {!subtract} *) val ( * ): Object.t -> Object.t -> Object.t (** Synomym of {!multiply} *) val ( / ): Object.t -> Object.t -> Object.t (** Synomym of {!true_divide} *) val ( ** ): Object.t -> Object.t -> Object.t (** Synomym of {!power} *) val ( ~- ): Object.t -> Object.t (** Synomym of {!negative} *) val ( land ): Object.t -> Object.t -> Object.t (** Synomym of {!number_and} *) val ( lor ): Object.t -> Object.t -> Object.t (** Synomym of {!number_or} *) val ( lxor ): Object.t -> Object.t -> Object.t (** Synomym of {!number_xor} *) val ( lsl ): Object.t -> Object.t -> Object.t (** Synomym of {!lshift} *) val ( lsr ): Object.t -> Object.t -> Object.t (** Synomym of {!rshift} *) end (** Interface for Python values of type [Run]. *) module Run: sig val eval: ?start:input -> ?globals:Object.t -> ?locals:Object.t -> string -> Object.t (** [eval ~start ~globals ~locals e] evaluates the Python expression [e] and returns the computed value. We have [Py.Run.eval ~start ~globals ~locals e = Py.Run.string e start globals locals]. @param start is the initial input mode (default: [Eval]). @param globals is the global symbol directory (default: [Py.Module.get_dict (Py.Module.main ())]). @param locals is the local symbol directory (default: [globals]). *) val load: ?start:input -> ?globals:Object.t -> ?locals:Object.t -> in_channel file -> string -> Object.t (** [load ~start ~globals ~locals chan filename] loads the contents of the file opened in [chan]. We have [Py.Run.load ~start ~globals ~locals chan filename = Py.Run.file chan filename start globals locals]. @param start is the initial input mode (default: [File]). @param globals is the global symbol directory (default: Module.get_dict (Module.main ())). @param locals is the local symbol directory (default: [Dict.create ()]). *) val interactive: unit -> unit (** Runs the interactive loop. We have [Py.Run.interactive () = Py.Run.interactive_loop stdin ""]. *) val ipython: ?frame:bool -> unit -> unit (** Runs the IPython interactive loop. *) val any_file: in_channel file -> string -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_AnyFile} PyRun_AnyFile} *) val file: in_channel file -> string -> input -> Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_File} PyRun_File} *) val interactive_one: in_channel -> string -> unit (** Channels suppose that the same C runtime has been used to compile both the Python library and the OCaml runtime. Warning: using channels is unsafe if runtimes differ (can lead to segmentation fault). Wrapper for {{:https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_InteractiveOne} PyRun_InteractiveOne} *) val interactive_loop: in_channel -> string -> unit (** Channels suppose that the same C runtime has been used to compile both the Python library and the OCaml runtime. Warning: using channels is unsafe if runtimes differ (can lead to segmentation fault). Wrapper for {{:https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_InteractiveLoop} PyRun_InteractiveLoop} *) val simple_file: in_channel file -> string -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_SimpleFile} PyRun_SimpleFile} *) val simple_string: string -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_SimpleString} PyRun_SimpleString} *) val string: string -> input -> Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/veryhigh.html#c.PyRun_String} PyRun_String} *) val frame: ('a -> 'b) -> 'a -> 'b end (** Interface for Python values with a [Sequence] interface. *) module Sequence: sig val check: Object.t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Check} PySequence_Check} *) val concat: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Concat} PySequence_Concat} *) val contains: Object.t -> Object.t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Contains} PySequence_Contains} *) val count: Object.t -> Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Count} PySequence_Count} *) val del_item: Object.t -> int -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_DelItem} PySequence_DelItem} *) val fast: Object.t -> string -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Fast} PySequence_Fast} *) val get_item: Object.t -> int -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_GetItem} PySequence_GetItem} *) val get: Object.t -> int -> Object.t (** Equivalent to {!get_item}. *) val get_slice: Object.t -> int -> int -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_GetSlice} PySequence_GetSlice} *) val index: Object.t -> Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Index} PySequence_Index} *) val in_place_concat: Object.t -> Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_InPlaceConcat} PySequence_InPlaceConcat} *) val in_place_repeat: Object.t -> int -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_InPlaceRepeat} PySequence_InPlaceRepeat} *) val length: Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Length} PySequence_Length} *) val list: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_List} PySequence_List} *) val repeat: Object.t -> int -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Repeat} PySequence_Repeat} *) val set_item: Object.t -> int -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_SetItem} PySequence_SetItem} *) val set: Object.t -> int -> Object.t -> unit (** Equivalent to {!set_item}. *) val set_slice: Object.t -> int -> int -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_SetSlice} PySequence_SetSlice} *) val size: Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Size} PySequence_Size} *) val tuple: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PySequence_Tuple} PySequence_Tuple} *) val to_array: Object.t -> Object.t array (** [to_array s] returns the array with the same elements as the Python sequence [s]. *) val to_array_map: (Object.t -> 'a) -> Object.t -> 'a array (** [to_array_map f s] returns the array of the results of [f] applied to all the elements of the Python sequence [s]. *) val to_list: Object.t -> Object.t list (** [to_list s] returns the list with the same elements as the Python sequence [s]. *) val to_list_map: (Object.t -> 'a) -> Object.t -> 'a list (** [to_list_map f s] returns the list of the results of [f] applied to all the elements of the Python sequence [s]. [to_list_map f s] is equivalent to [List.map f (to_list s)] but is tail-recursive and [f] is applied to the elements of [s] in the reverse order. *) val to_seq: Object.t -> Object.t Stdcompat.Seq.t (** [to_seq s] returns the OCaml sequence of the values from the Python sequence [s]. *) val to_seqi: Object.t -> (int * Object.t) Stdcompat.Seq.t (** [to_seqi s] returns the OCaml indexed sequence of the values from the Python sequence [s]. *) val fold_left: ('a -> Object.t -> 'a) -> 'a -> Object.t -> 'a (** [fold_left f v s] returns [(f (...(f v s1)...) sn)] where [s1], ..., [sn] are the elements of the Python sequence [s]. *) val fold_right: (Object.t -> 'a -> 'a) -> Object.t -> 'a -> 'a (** [fold_right f s v] returns [(f s1 (...(f v sn)...)] where [s1], ..., [sn] are the elements of the Python sequence [s]. This function is tail-recursive. *) val for_all: (Object.t -> bool) -> Object.t -> bool (** [for_all p s] checks if [p] holds for all the elements of the Python sequence [s]. *) val exists: (Object.t -> bool) -> Object.t -> bool (** [exists p s] checks if [p] holds for at least one of the elements of the Python sequence [s]. *) end type byteorder = LittleEndian | BigEndian (** Interface for Python values of type [String], [Bytes] and [Unicode]. *) module String: sig val check: Object.t -> bool (** [check o] returns [o] if [o] is a Python string (either [Bytes] or [Unicode] with Python 3). *) val check_bytes: Object.t -> bool (** [check_bytes o] returns [o] if [o] is a Python bytes string. *) val check_unicode: Object.t -> bool (** [check_unicode o] returns [o] if [o] is a Python unicode string. *) val format: Object.t -> Object.t -> Object.t (** [format fmt args] returns the formatted Python string from the string format [fmt] and the arguments [args]. This is analogous to [fmt % args]. With Python 2, if [fmt] is a String, wrapper for {{:https://docs.python.org/2/c-api/string.html#c.PyString_Format} PyString_Format}. With Python 3 or with Python 2 if [fmt] is Unicode, wrapper for {{:https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_Format} PyUnicode_Format}. *) val as_UTF8_string: Object.t -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_AsUTF8String} PyUnicode_AsUTF8String} *) val decode_UTF8: ?errors:string -> ?size:int -> string -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF8} PyUnicode_DecodeUTF8}. If [size] is omitted, the length of the string is used by default. *) val decode_UTF16: ?errors:string -> ?size:int -> ?byteorder:byteorder -> string -> Object.t * byteorder (** Wrapper for {{:https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF16} PyUnicode_DecodeUTF16}. If [size] is omitted, the length of the string is used by default. *) val decode_UTF32: ?errors:string -> ?size:int -> ?byteorder:byteorder -> string -> Object.t * byteorder (** Wrapper for {{:https://docs.python.org/3/c-api/unicode.html#c.PyUnicode_DecodeUTF32} PyUnicode_DecodeUTF32}. If [size] is omitted, the length of the string is used by default. *) val length: Object.t -> int (** [length s] returns the length of the Python string [s]. A failure ([Failure _]) is raised if [s] is neither a [Bytes] value nor a [Unicode] value. With Python 2, if [s] is a String, wrapper for {{:https://docs.python.org/2/c-api/string.html#c.PyString_Size} PyString_Size}, and if [s] is Unicode, wrapper for {{:https://docs.python.org/2/c-api/unicode.html#c.PyUnicode_GetSize} PyUnicode_GetSize}, With Python 3, if [s] is Bytes, wrapper for {{:https://docs.python.org/2/c-api/bytes.html#c.PyBytes_Size} PyBytes_Size}, and if [s] is Unicode, wrapper for {{:https://docs.python.org/2/c-api/unicode.html#c.PyUnicode_GetLength} PyUnicode_GetLength}. *) val of_string: string -> Object.t (** [of_string s] returns the Python string with the value [s]. [s] should be a valid UTF-8 string. *) val of_bytes: Stdcompat.bytes -> Object.t (** Same as [of_string] but with an argument of type [bytes]. *) val to_string: Object.t -> string (** [to_string o] returns the string contained in the Python value [o]. A failure ([Failure _]) is raised if [o] is neither a [String]/[Bytes] value nor a [Unicode] value. *) val to_bytes: Object.t -> Stdcompat.bytes (** Same as [to_string] but with an a result of type [bytes]. *) val of_unicode: ?size:int -> int array -> Object.t (** [of_unicode codepoints] returns the Python Unicode string with the codepoints [codepoints]. *) val to_unicode: Object.t -> int array (** [to_unicode s] returns the codepoints of the Python Unicode string [s]. *) end (** Interface for Python values of type [Bytes]. With Python 2, aliases for [String]. *) module Bytes: sig val of_string: string -> Object.t (** [of_string s] returns the Python byte sequence with the contents of [s]. *) val of_bytes: Stdcompat.bytes -> Object.t (** Same as [of_string] but with an argument of type [bytes]. *) val to_string: Object.t -> string (** [to_string o] returns the string contained in the Python value [o]. *) val to_bytes: Object.t -> Stdcompat.bytes (** Same as [to_string] but with an a result of type [bytes]. *) val length: Object.t -> int (** [length s] returns the length of the Python byte sequence [s]. *) end (** Interface for Python values of type [Tuple]. *) module Tuple: sig val check: Object.t -> bool (** [check o] returns [true] if [o] is a Python tuple. *) val create: int -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/tuple.html#c.PyTuple_New} PyTuple_New} *) val empty: Object.t (** The empty tuple [()]. This value is guaranteed to be the unique value associated to [()]. *) 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]. *) val get_item: Object.t -> int -> Object.t (** Equivalent to {!Sequence.get_item}. *) val get: Object.t -> int -> Object.t (** Equivalent to {!get_item}. *) val set_item: Object.t -> int -> Object.t -> unit (** Wrapper for {{:https://docs.python.org/3/c-api/sequence.html#c.PyTuple_SetItem} PyTuple_SetItem} *) val set: Object.t -> int -> Object.t -> unit (** Equivalent to {!set_item}. *) val get_slice: Object.t -> int -> int -> Object.t (** Wrapper for {{:https://docs.python.org/3/c-api/tuple.html#c.PyTuple_GetSlice} PyTuple_GetSlice} *) val size: Object.t -> int (** Wrapper for {{:https://docs.python.org/3/c-api/tuple.html#c.PyTuple_Size} PyTuple_Size} *) val init: int -> (int -> Object.t) -> Object.t (** [init n f] returns the Python tuple [(f 0, f 1, ..., f (n - 1))]. *) val of_array: Object.t array -> Object.t (** [of_array a] returns the Python tuple with the same elements as [a]. *) val of_array_map: ('a -> Object.t) -> 'a array -> Object.t (** [of_array_map f a] returns the Python tuple [(f a0, ..., f ak)] where [a0], ..., [ak] are the elements of [a]. *) val of_list: Object.t list -> Object.t (** [of_list l] returns the Python tuple with the same elements as [l]. *) val of_list_map: ('a -> Object.t) -> 'a list -> Object.t (** [of_list f l] returns the Python tuple [(f l1, ..., f ln)] where [l1], ..., [ln] are the elements of [l]. [of_list_map f l] is equivalent to [of_list (List.map f l)] but is tail-recursive. *) val to_array: Object.t -> Object.t array (** Equivalent to {!Sequence.to_array}. *) val to_array_map: (Object.t -> 'a) -> Object.t -> 'a array (** Equivalent to {!Sequence.to_array_map}. *) val to_list: Object.t -> Object.t list (** Equivalent to {!Sequence.to_list}. *) val to_list_map: (Object.t -> 'a) -> Object.t -> 'a list (** Equivalent to {!Sequence.to_list_map}. *) val of_seq: Object.t Stdcompat.Seq.t -> Object.t (** [of_seq s] returns the Python tuple with the values of the sequence s. *) val to_seq: Object.t -> Object.t Stdcompat.Seq.t (** Equivalent to {!Sequence.to_seq}. *) val to_seqi: Object.t -> (int * Object.t) Stdcompat.Seq.t (** Equivalent to {!Sequence.to_seqi}. *) val fold_left: ('a -> Object.t -> 'a) -> 'a -> Object.t -> 'a (** Equivalent to {!Sequence.fold_left}. *) val fold_right: (Object.t -> 'a -> 'a) -> Object.t -> 'a -> 'a (** Equivalent to {!Sequence.fold_right}. *) val for_all: (Object.t -> bool) -> Object.t -> bool (** Equivalent to {!Sequence.for_all}. *) val exists: (Object.t -> bool) -> Object.t -> bool (** Equivalent to {!Sequence.exists}. *) val of_sequence: Object.t -> Object.t (** Equivalent to {!Sequence.tuple}. *) val of_tuple1: Object.t -> Object.t (** [of_tuple1 o0] returns the Python tuple [(o0)]. *) val of_tuple2: Object.t * Object.t -> Object.t (** [of_tuple4 (o0, o1)] returns the Python tuple [(o0, o1)]. *) val of_tuple3: Object.t * Object.t * Object.t -> Object.t (** [of_tuple4 (o0, o1, o2)] returns the Python tuple [(o0, o1, o2)]. *) val of_tuple4: Object.t * Object.t * Object.t * Object.t -> Object.t (** [of_tuple4 (o0, o1, o2, o3)] returns the Python tuple [(o0, o1, o2, o3)]. *) val of_tuple5: Object.t * Object.t * Object.t * Object.t * Object.t -> Object.t (** [of_tuple5 (o0, o1, o2, o3, o4)] returns the Python tuple [(o0, o1, o2, o3, o4)]. *) val to_tuple1: Object.t -> Object.t (** [to_tuple1 t] returns the value [Py.Tuple.get_item t 0]. *) val to_tuple2: Object.t -> Object.t * Object.t (** [to_tuple5 t] returns the tuple [(Py.Tuple.get_item t 0, Py.Tuple.get_item t 1)]. *) val to_tuple3: Object.t -> Object.t * Object.t * Object.t (** [to_tuple5 t] returns the tuple [(Py.Tuple.get_item t 0, Py.Tuple.get_item t 1, Py.Tuple.get_item t 2)]. *) val to_tuple4: Object.t -> Object.t * Object.t * Object.t * Object.t (** [to_tuple5 t] returns the tuple [(Py.Tuple.get_item t 0, Py.Tuple.get_item t 1, Py.Tuple.get_item t 2, Py.Tuple.get_item t 3)]. *) val to_tuple5: Object.t -> Object.t * Object.t * Object.t * Object.t * Object.t (** [to_tuple5 t] returns the tuple [(Py.Tuple.get_item t 0, Py.Tuple.get_item t 1, Py.Tuple.get_item t 2, Py.Tuple.get_item t 3, Py.Tuple.get_item t 4)]. *) val singleton: Object.t -> Object.t (** Equivalent to {!of_tuple1}. *) val to_singleton: Object.t -> Object.t (** Equivalent to {!to_tuple1}. *) val of_pair: Object.t * Object.t -> Object.t (** Equivalent to {!of_tuple2}. *) val to_pair: Object.t -> Object.t * Object.t (** Equivalent to {!to_tuple2}. *) end (** Introspection of Python types *) module Type: sig type t = Unknown | Bool | Bytes | Callable | Capsule | Closure | Dict | Float | List | Int | Long | Module | None | Null | Tuple | Type | Unicode | Iter | Set (** Some types of Python values. [Bytes] covers both the [Str] values of Python 2 and the [Bytes] values of Python 3. [Long] covers both the [Int] values of Python 2 and the [Long] values of Python 3. [Capsule] corresponds to the values created with {!Py.Capsule}. [Closure] corresponds to the values created with {!Py.Callable}. *) val get: Object.t -> t (** [get o] returns the type of the Python value [o]. *) val is_subtype: Object.t -> Object.t -> bool (** Wrapper for {{:https://docs.python.org/3/c-api/type.html#c.PyType_IsSubtype} PyType_IsSubtype} *) val is_none: Object.t -> bool (** [is_none o] returns [true] if the Python object [o] is [None]. *) val name: t -> string (** [name t] returns a string that represents the type [t]. *) val mismatch: string -> Object.t -> 'a (** [mismatch ty obj] raises a type mismatch [Failure _] that indicates that an object of type [ty] was expected, but [obj] was found. *) val create: string -> Object.t list -> (string * Object.t) list -> Object.t (** [create classname parents dict] calls Python [type()] function to create a new type [classname] deriving from [parents] with the dictionary [dict]. *) end module Marshal: sig val read_object_from_file: in_channel file -> Object.t (** [read_object_from_file f] reads one value from [f] and returns it. Wrapper for {{:https://docs.python.org/3/c-api/marshal.html#c.PyMarshal_ReadObjectFromFile} PyMarshal_ReadObjectFromFile} *) val load: in_channel file -> Object.t (** Equivalent to {!read_object_from_file}. *) val read_last_object_from_file: in_channel file -> Object.t (** [read_last_object_from_file f] reads a value from [f] and returns it. That value should be the only value remaining to be read from [f] before EOF. Wrapper for {{:https://docs.python.org/3/c-api/marshal.html#c.PyMarshal_ReadLastObjectFromFile} PyMarshal_ReadLastObjectFromFile} *) val read_object_from_string: string -> int -> Object.t (** [read_object_from_string s len] reads a value from the [len] first bytes of [s]. Wrapper for {{:https://docs.python.org/3/c-api/marshal.html#c.PyMarshal_ReadObjectFromString} PyMarshal_ReadObjectFromString} *) val loads: string -> Object.t (** [Py.Marshal.loads s] is equivalent to [Py.Marshal.read_object_from_string s (String.length s)]. *) val write_object_to_file: Object.t -> out_channel file -> int -> unit (** [write_object_to_file value file version] writes the object [value] to [file]. [version] indicates the file format (use {!version} to get the current version). Wrapper for {{:https://docs.python.org/3/c-api/marshal.html#c.PyMarshal_WriteObjectToFile} PyMarshal_WriteObjectToFile} *) val dump: ?version:int -> Object.t -> out_channel file -> unit (** [Py.Marshal.dump ?version value file] is equivalent to [Py.Marshal.write_object_to_file value file version]. By default, the version returned by {!version} is used. *) val write_object_to_string: Object.t -> int -> Object.t (** [write_object_to_file value file version] returns the Python string representing the object [value]. [version] indicates the format (use {!version} to get the current version). Wrapper for {{:https://docs.python.org/3/c-api/marshal.html#c.PyMarshal_WriteObjectToString} PyMarshal_WriteObjectToString} *) val dumps: ?version:int -> Object.t -> string (** [Py.Marshal.dumps ?version value] is equivalent to [Py.String.to_string (Py.Marshal.write_object_to_string value version)]. By default, the version returned by {!version} is used. *) val version: unit -> int (** Returns the current file format version number. *) end module Array: sig val of_indexed_structure: (int -> Object.t) -> (int -> Object.t -> unit) -> int -> Object.t (** [Py.Array.of_indexed_structure getter setter length] returns a Python array-like structure [a] of length [length], such that reading [a[i]] returns [getter i] and [a[i] = v] calls [setter i v]. To make the array-like structure read-only, raise an exception in [setter]. *) val of_array: ('a -> Object.t) -> (Object.t -> 'a) -> 'a array -> Object.t (** [Py.Array.of_array getter setter array] returns a Python array-like structure accessing the elements of [array] via [getter] and [setter]. To make the array-like structure read-only, raise an exception in [setter]. *) val numpy_api: unit -> Object.t (** Returns the object which contains the entry points to the Numpy API. It is used internally by the following functions and by the {!Numpy} module. *) val pyarray_type: unit -> Object.t (** Returns the type of Numpy arrays. *) val numpy: Stdcompat.floatarray -> Object.t (** [numpy a] returns a Numpy array that shares the same contents than the OCaml array [a]. The array is passed in place (without copy) which relies on the unboxed representation of [floatarray] : Python programs can change the contents of the array and the changes are visible in the OCaml array. Note that the {!Numpy} module provides a more general interface between Numpy arrays and OCaml bigarrays. *) val numpy_get_array: Object.t -> Stdcompat.floatarray (** [numpy_get_array a] returns the OCaml array from which the Numpy array [a] has been converted from. Note that this function fails if [a] has not been obtained by calling the {!numpy} function above. If you need to convert an arbitrary Numpy array to OCaml, you should use bigarrays and the {!Numpy} module. *) end module Gil : sig type t val ensure : unit -> t (** [ensure ()] ensures that the current thread holds the global interpreter lock and hence can call the Python C API in a safe way. Wrapper for {{::https//docs.python.org/3/c-api/init.html#c.PyGILState_Ensure} PyGILState_Ensure} *) val release : t -> unit (** [release t] releases any resource acquired by [ensure]. Wrapper for {{::https//docs.python.org/3/c-api/init.html#c.PyGILState_Release} PyGILState_Release} *) val check : unit -> bool (** [check ()] returns true if the current thread holds the global interpreter lock. Wrapper for {{::https//docs.python.org/3/c-api/init.html#c.PyGILState_Check} PyGILState_Check} *) val with_lock : (unit -> 'a) -> 'a (** [with_lock f] runs [f] ensuring that we hold the global interpreter lock to do so. If the lock needs to be acquired it is released once [f] completes or if [f] raises an exception. *) end val set_argv: string array -> unit (** [set_argv argv] set Python's [sys.argv]. *) val last_value: unit -> Object.t (** [last_value ()] returns the last value that was computed in the toplevel. We have [Py.last_value = Py.Module.find (Py.Module.builtins ()) "_"]. *) val exception_printer: exn -> string option (** This printer pretty-prints [E (ty, value)] exceptions. It is automatically registered to [Printexc.register_printer]. *) val compile: source:string -> filename:string -> ?dont_inherit:bool -> ?optimize:[`Default | `Debug | `Normal | `RemoveDocstrings ] -> [`Exec | `Eval | `Single] -> Object.t (** Old interface for {!val:Py.Module.compile}. *) pycaml.ml000066400000000000000000000546141452051003100126760ustar00rootroot00000000000000type pyobject = Py.Object.t type pyobject_type = | TupleType | BytesType | UnicodeType | BoolType | IntType | FloatType | ListType | NoneType | CallableType | ModuleType | ClassType | TypeType | DictType | NullType | CamlpillType | OtherType | EitherStringType (* Signifies that either of BytesType or UnicodeType is allowed. *) | CamlpillSubtype of string (* Signifies that only the particular Camlpill variety is allowed. *) | AnyType let pytype_name t = match t with | TupleType -> "Python-Tuple" | BytesType -> "Python-Bytes" | UnicodeType -> "Python-Unicode" | BoolType -> "Python-Bool" | IntType -> "Python-Int" | FloatType -> "Python-Float" | ListType -> "Python-List" | NoneType -> "Python-None" | CallableType -> "Python-Callable" | ModuleType -> "Python-Module" | ClassType -> "Python-Class" | NullType -> "Python-Null" | TypeType -> "Python-Type" | DictType -> "Python-Dict" | CamlpillType -> "Python-Camlpill" | OtherType -> "Python-Other" | EitherStringType -> "Python-EitherString" | CamlpillSubtype sym -> "Python-Camlpill-" ^ sym | AnyType -> "Python-Any" let _py_type_of_pyobject_type t = match t with | BoolType -> Py.Type.Bool | BytesType -> Py.Type.Bytes | CallableType -> Py.Type.Callable | CamlpillType | CamlpillSubtype _ -> Py.Type.Capsule | DictType -> Py.Type.Dict | FloatType -> Py.Type.Float | ListType -> Py.Type.List | IntType -> Py.Type.Long | ModuleType -> Py.Type.Module | NoneType -> Py.Type.None | NullType -> Py.Type.Null | TupleType -> Py.Type.Tuple | TypeType -> Py.Type.Type | EitherStringType | UnicodeType -> Py.Type.Unicode | AnyType | ClassType | OtherType -> Py.Type.Unknown let pyobject_type_of_py_type t = match t with Py.Type.Unknown | Py.Type.Iter | Py.Type.Set -> OtherType | Py.Type.Bool -> BoolType | Py.Type.Bytes -> BytesType | Py.Type.Callable -> CallableType | Py.Type.Capsule -> CamlpillType | Py.Type.Closure -> CallableType | Py.Type.Dict -> DictType | Py.Type.Float -> FloatType | Py.Type.List -> ListType | Py.Type.Int | Py.Type.Long -> IntType | Py.Type.Module -> ModuleType | Py.Type.None -> NoneType | Py.Type.Null -> NullType | Py.Type.Tuple -> TupleType | Py.Type.Type -> TypeType | Py.Type.Unicode -> UnicodeType type pyerror_type = Pyerr_Exception | Pyerr_StandardError | Pyerr_ArithmeticError | Pyerr_LookupError | Pyerr_AssertionError | Pyerr_AttributeError | Pyerr_EOFError | Pyerr_EnvironmentError | Pyerr_FloatingPointError | Pyerr_IOError | Pyerr_ImportError | Pyerr_IndexError | Pyerr_KeyError | Pyerr_KeyboardInterrupt | Pyerr_MemoryError | Pyerr_NameError | Pyerr_NotImplementedError | Pyerr_OSError | Pyerr_OverflowError | Pyerr_ReferenceError | Pyerr_RuntimeError | Pyerr_SyntaxError | Pyerr_SystemExit | Pyerr_TypeError | Pyerr_ValueError | Pyerr_ZeroDivisionError include Pywrappers.Pycaml exception Pycaml_exn of (pyerror_type * string) let make_pill_wrapping name _instance = Py.Capsule.make name let py_false () = Py.Bool.f let py_finalize = Py.finalize let py_initialize () = Py.initialize () let py_is_true = Py.Object.is_true let py_isinitialized () = if Py.is_initialized () then 1 else 0 let py_setprogramname = Py.set_program_name let py_setpythonhome = Py.set_python_home let py_getprogramname = Py.get_program_name let py_getpythonhome = Py.get_python_home let py_getprogramfullpath = Py.get_program_full_path let py_getprefix = Py.get_prefix let py_getexecprefix = Py.get_exec_prefix let py_getpath = Py.get_path let py_true () = Py.Bool.t let _pycaml_seterror error msg = let error' = match error with Pyerr_Exception -> Py.Err.Exception | Pyerr_StandardError -> Py.Err.StandardError | Pyerr_ArithmeticError -> Py.Err.ArithmeticError | Pyerr_LookupError -> Py.Err.LookupError | Pyerr_AssertionError -> Py.Err.AssertionError | Pyerr_AttributeError -> Py.Err.AttributeError | Pyerr_EOFError -> Py.Err.EOFError | Pyerr_EnvironmentError -> Py.Err.EnvironmentError | Pyerr_FloatingPointError -> Py.Err.FloatingPointError | Pyerr_IOError -> Py.Err.IOError | Pyerr_ImportError -> Py.Err.ImportError | Pyerr_IndexError -> Py.Err.IndexError | Pyerr_KeyError -> Py.Err.KeyError | Pyerr_KeyboardInterrupt -> Py.Err.KeyboardInterrupt | Pyerr_MemoryError -> Py.Err.MemoryError | Pyerr_NameError -> Py.Err.NameError | Pyerr_NotImplementedError -> Py.Err.NotImplementedError | Pyerr_OSError -> Py.Err.OSError | Pyerr_OverflowError -> Py.Err.OverflowError | Pyerr_ReferenceError -> Py.Err.ReferenceError | Pyerr_RuntimeError -> Py.Err.RuntimeError | Pyerr_SyntaxError -> Py.Err.SyntaxError | Pyerr_SystemExit -> Py.Err.SystemExit | Pyerr_TypeError -> Py.Err.TypeError | Pyerr_ValueError -> Py.Err.ValueError | Pyerr_ZeroDivisionError -> Py.Err.ZeroDivisionError in Py.Err.set_error error' msg let int_of_bool b = if b then -1 else 0 let _pybytes_check v = int_of_bool (Py.Type.get v = Py.Type.Bytes) let pybytes_asstring = Py.String.to_string let pybytes_format (fmt, args) = Py.String.format fmt args let pyiter_check v = int_of_bool (Py.Type.get v = Py.Type.Iter) let pymodule_getfilename = Py.Module.get_filename let pymodule_getname = Py.Module.get_name let _pyunicode_check v = int_of_bool (Py.Type.get v = Py.Type.Unicode) let pyerr_fetch _ = match Py.Err.fetch () with None -> (Py.null, Py.null, Py.null) | Some e -> e let pyerr_normalizeexception e = e let pylist_toarray = Py.Sequence.to_array let pyint_asint = Py.Long.to_int let pyint_fromint = Py.Long.of_int let pynone () = Py.none let pynull () = Py.null let pystring_asstring = Py.String.to_string let pystring_fromstring = Py.String.of_string let pytuple_fromarray = Py.Tuple.of_array let pytuple_fromsingle = Py.Tuple.singleton let pytuple_toarray = Py.Tuple.to_array let pytype v = pyobject_type_of_py_type (Py.Type.get v) let register_ocamlpill_types _array = () let pyeval_callobject (func, arg) = Pywrappers.pyeval_callobjectwithkeywords func arg Py.null let pyimport_execcodemodule (obj, s) = Pywrappers.pyimport_execcodemodule s obj let pyimport_importmoduleex (name, globals, locals, fromlist) = Pywrappers.pyimport_importmodulelevel name globals locals fromlist (-1) let py_compilestringflags (str, filename, start, flags) = if Py.version_major () <= 2 then py_compilestringflags (str, filename, start, flags) else py_compilestringexflags (str, filename, start, flags, -1) let py_compilestring (str, filename, start) = py_compilestringflags (str, filename, start, None) let pyrun_anyfile (fd, filename) = pyrun_anyfileexflags (fd, filename, 0, None) let pyrun_anyfileex (fd, filename, closeit) = pyrun_anyfileexflags (fd, filename, closeit, None) let pyrun_file (fd, filename, start, globals, locals) = pyrun_fileexflags (fd, filename, start, globals, locals, 0, None) let pyrun_fileex (fd, filename, start, globals, locals, closeit) = pyrun_fileexflags (fd, filename, start, globals, locals, closeit, None) let pyrun_interactiveone (fd, filename) = pyrun_interactiveoneflags (fd, filename, None) let pyrun_interactiveloop (fd, filename) = pyrun_interactiveloopflags (fd, filename, None) let pyrun_simplefile (fd, filename) = pyrun_simplefileexflags (fd, filename, 0, None) let pyrun_simplefileex (fd, filename, closeit) = pyrun_simplefileexflags (fd, filename, closeit, None) let pyrun_simplestring s = pyrun_simplestringflags (s, None) let pyrun_string (s, start, globals, locals) = pyrun_stringflags (s, start, globals, locals, None) let pywrap_closure f = Py.Callable.of_function_as_tuple f let pytuple_empty = Py.Tuple.empty let pytuple2 = Py.Tuple.of_tuple2 let pytuple3 = Py.Tuple.of_tuple3 let pytuple4 = Py.Tuple.of_tuple4 let pytuple5 = Py.Tuple.of_tuple5 let set_python_argv = Py.set_argv let py_optionally unwrapper py_value = if not (Py.List.check py_value) then Py.Type.mismatch "List" py_value; match Py.List.size py_value with 0 -> None | 1 -> Some (unwrapper (Py.List.get_item py_value 0)) | _ -> Py.Type.mismatch "List of size 0 or 1" py_value let pycallable_asfun = Py.Callable.to_function let guarded_pytuple_toarray = Py.Tuple.to_array let guarded_pylist_toarray = Py.List.to_array let guarded_pybytes_asstring = Py.String.to_string let guarded_pynumber_asfloat = Py.Number.to_float let guarded_pyfloat_asfloat = Py.Float.to_float let guarded_pyint_asint = Py.Long.to_int let ocamlpill_hard_unwrap v = snd (Py.Capsule.unsafe_unwrap_value v) let python_eval = pyrun_simplestring let python_load filename = ignore (Py.Run.load (Py.Filename filename) filename) let pybytes_asstringandsize = Py.String.to_string let pystring_asstringandsize = Py.String.to_string let pyunicode_decodeutf8 (s, errors) = Py.String.decode_UTF8 s ?errors let byteorder_of_int_option byteorder_int = match byteorder_int with None -> None | Some (-1) -> Some Py.LittleEndian | Some 1 -> Some Py.BigEndian | _ -> failwith "pyunicode_decodeutf: invalid byteorder" let pyunicode_decodeutf16 (s, errors, byteorder_int) = let byteorder = byteorder_of_int_option byteorder_int in fst (Py.String.decode_UTF16 s ?errors ?byteorder) let pyunicode_decodeutf32 (s, errors, byteorder_int) = let byteorder = byteorder_of_int_option byteorder_int in fst (Py.String.decode_UTF32 s ?errors ?byteorder) let pyunicode_fromunicode f size = Py.String.of_unicode (Array.init size f) let pyunicode_asunicode = Py.String.to_unicode let pyunicode_getsize = Py.String.length let pyobject_ascharbuffer = Py.Object.as_char_buffer let pyobject_asreadbuffer = Py.Object.as_read_buffer let pyobject_aswritebuffer = Py.Object.as_write_buffer let python () = Py.Run.interactive (); 0 let ipython () = Py.Run.ipython (); 0 let make_ocamlpill_wrapper_unwrapper = make_pill_wrapping let ocamlpill_type_of = Py.Capsule.type_of let type_mismatch_exception type_wanted type_here pos exn_name = Pycaml_exn (Pyerr_TypeError, (Printf.sprintf "Argument %d: Type wanted: %s -- Type provided: %s%s." (pos + 1) (pytype_name type_wanted) (pytype_name type_here) exn_name)) let pill_type_mismatch_exception ?position ?exn_name wanted gotten = let arg_no = match position with None -> "" | Some _p -> "Argument %d: " in let en = match exn_name with None -> "" | Some n -> n in Pycaml_exn (Pyerr_TypeError, Printf.sprintf "%sPython-Ocaml Pill Type mismatch: wanted: '%s' - got: '%s'%s" arg_no wanted gotten en) let check_pill_type ?position ?exn_name wanted pill = let gotten = ocamlpill_type_of pill in if not (gotten = wanted) then raise (pill_type_mismatch_exception ?position:position ?exn_name:exn_name wanted gotten) let unpythonizing_function ?name ?(catch_weird_exceptions = true) ?extra_guards ?(expect_tuple = false) wanted_types function_body = ignore catch_weird_exceptions; let exn_name = match name with None -> "" | Some s -> Printf.sprintf " (%s)" s in let work_fun python_args = let body () = let nr_args_given = if expect_tuple then pytuple_size python_args else 1 in let nr_args_wanted = Array.length wanted_types in let () = if nr_args_given <> nr_args_wanted then raise (Pycaml_exn (Pyerr_IndexError, (Printf.sprintf "Args given: %d Wanted: %d%s" nr_args_given nr_args_wanted exn_name))) in let arr_args = if expect_tuple then Py.Tuple.to_array python_args else [| python_args |] in let rec check_types pos = if pos = nr_args_given then function_body arr_args else let arg = arr_args.(pos) in let type_wanted = wanted_types.(pos) in let () = match type_wanted with | AnyType -> () | EitherStringType -> if not (Py.String.check arg) then raise (type_mismatch_exception type_wanted (pytype arg) pos exn_name) | CamlpillSubtype sym -> check_pill_type ~position:pos ~exn_name:exn_name sym arg | _ -> let type_here = pytype arg in if type_here <> type_wanted then raise (type_mismatch_exception type_wanted type_here pos exn_name) in begin match extra_guards with None -> () | Some guards -> let guard = guards.(pos) in let guard_error = guard arr_args.(pos) in match guard_error with None -> () | Some msg -> raise (Pycaml_exn (Pyerr_TypeError, (Printf.sprintf "Check for argument %d failed: %s%s" (pos + 1) msg exn_name))) end; check_types (pos+1) in check_types 0 in body () in work_fun let py_profiling_active = ref false let py_profile_hash = Stdcompat.Lazy.from_fun (fun () -> Hashtbl.create 100) let py_activate_profiling () = let old_value = !py_profiling_active in py_profiling_active := true; old_value let py_deactivate_profiling () = let old_value = !py_profiling_active in py_profiling_active := false; old_value let py_profile_report () = let add_entry name time_and_calls list = (name, time_and_calls.(0), time_and_calls.(1)) :: list in let items = Hashtbl.fold add_entry (Lazy.force py_profile_hash) [] in let array = Array.of_list items in let order (_, time_a, _) (_, time_b, _) = compare time_b time_a in Array.sort order array; array let py_profile_reset () = Hashtbl.clear (Lazy.force py_profile_hash) let python_interfaced_function ?name ?(catch_weird_exceptions = true) ?doc ?extra_guards wanted_types function_body = let exn_name = match name with None -> "" | Some s -> Printf.sprintf " (%s)" s in let closure = unpythonizing_function ?name ~catch_weird_exceptions ?extra_guards wanted_types function_body in let closure' args = try closure args with Not_found -> let msg = Printf.sprintf "OCaml exception 'Not_found'%s" exn_name in raise (Py.Err (Py.Err.LookupError, msg)) | Division_by_zero -> let msg = Printf.sprintf "OCaml exception 'Division_by_zero'%s" exn_name in raise (Py.Err (Py.Err.ZeroDivisionError, msg)) | Failure s -> let msg = Printf.sprintf "OCaml exception 'Failure: %s'%s" s exn_name in raise (Py.Err (Py.Err.StandardError, msg)) | Invalid_argument s -> let msg = Printf.sprintf "OCaml exception 'Invalid_argument: %s'%s" s exn_name in raise (Py.Err (Py.Err.StandardError, msg)) | Out_of_memory -> let msg = Printf.sprintf "OCaml exception 'Out_of_memory'%s" exn_name in raise (Py.Err (Py.Err.MemoryError, msg)) | Stack_overflow -> let msg = Printf.sprintf "OCaml exception 'Stack_overflow'%s" exn_name in raise (Py.Err (Py.Err.OverflowError, msg)) | Sys_error s -> let msg = Printf.sprintf "OCaml exception 'Sys_error: %s'%s" s exn_name in raise (Py.Err (Py.Err.StandardError, msg)) | End_of_file -> let msg = Printf.sprintf "OCaml exception 'End_of_file'%s" exn_name in raise (Py.Err (Py.Err.IOError, msg)) | Match_failure (filename, line, column) -> let msg = Printf.sprintf "OCaml exception 'Match_faiure file=%s line=%d(c. %d)'%s" filename line column exn_name in raise (Py.Err (Py.Err.StandardError, msg)) | Assert_failure (filename, line, column) -> let msg = Printf.sprintf "OCaml exception 'Assert_faiure file=%s line=%d(c. %d)'%s" filename line column exn_name in raise (Py.Err (Py.Err.StandardError, msg)) | Py.E (_, _) | Py.Err (_, _) as e -> raise e | something_else when catch_weird_exceptions -> let msg = Printf.sprintf "OCaml weird low-level exception '%s'%s" (Printexc.to_string something_else) exn_name in raise (Py.Err (Py.Err.StandardError, msg)) in let closure'' = match name with Some s when !py_profiling_active -> let closure'' args = let t0 = Unix.gettimeofday () in let stop_timer () = let t1 = Unix.gettimeofday () in let time_and_calls = let py_profile_hash' = Lazy.force py_profile_hash in try Hashtbl.find py_profile_hash' s with Not_found -> let x = [| 0.; 0. |] in Hashtbl.add py_profile_hash' s x; x in time_and_calls.(0) <- time_and_calls.(0) +. t1 -. t0; time_and_calls.(1) <- time_and_calls.(1) +. 1. in try let result = closure' args in stop_timer (); result with e -> stop_timer (); raise e in closure'' | _ -> closure' in Py.Callable.of_function_as_tuple ?docstring:doc closure'' let python_pre_interfaced_function ?catch_weird_exceptions ?doc ?extra_guards wanted_types function_body name = python_interfaced_function ~name ?catch_weird_exceptions ?doc ?extra_guards wanted_types function_body let pythonize_string = Py.String.of_string let unpythonize_string = Py.String.to_string let py_homogeneous_list_as_array ?error_label ?length type_name type_checker unwrapper list = let the_error_label = match error_label with None -> "" | Some x -> Printf.sprintf "%s: " x in let list_size = Py.List.size list in let () = match length with None -> () | Some length' -> if list_size <> length' then raise (Pycaml_exn (Pyerr_TypeError, (Printf.sprintf "%sExpected list of length %d, got length: %d" the_error_label length' list_size))) in for i = 0 to list_size - 1 do let item = Py.List.get list i in if not (type_checker item) then raise (Pycaml_exn (Pyerr_TypeError, Printf.sprintf "%sExpected homogeneous list of %s. Entry %d is of type %s (%s)!" the_error_label type_name (1 + i) (Py.Type.name (Py.Type.get item)) (Py.Object.string_of_repr item))) done; Py.List.to_array_map unwrapper list let py_float_list_as_array ?error_label ?length arr = py_homogeneous_list_as_array ?error_label ?length "float" Py.Float.check pyfloat_asdouble arr let py_int_list_as_array ?error_label ?length arr = py_homogeneous_list_as_array ?error_label ?length "int" Py.Long.check pyint_asint arr let py_number_list_as_float_array ?error_label ?length arr = py_homogeneous_list_as_array ?error_label ?length "number" Py.Number.check Py.Number.to_float arr let py_string_list_as_array ?error_label ?length arr = py_homogeneous_list_as_array ?error_label ?length "string" Py.String.check Py.String.to_string arr let py_list_list_as_array_map ?error_label ?length map arr = py_homogeneous_list_as_array ?error_label ?length "" Py.List.check map arr let py_list_list_as_array ?error_label ?length arr = py_list_list_as_array_map ?error_label ?length (fun x -> x) arr let py_list_list_as_array2 ?error_label ?length arr = py_list_list_as_array_map ?error_label ?length Py.List.to_array arr let py_float_list_list_as_array ?error_label ?length_outer ?length_inner arr = py_list_list_as_array_map ?error_label ?length:length_outer (py_float_list_as_array ?error_label ?length:length_inner) arr let py_number_list_list_as_float_array ?error_label ?length_outer ?length_inner arr = py_list_list_as_array_map ?error_label ?length:length_outer (py_number_list_as_float_array ?error_label ?length:length_inner) arr let py_int_list_list_as_array ?error_label ?length_outer ?length_inner arr = py_list_list_as_array_map ?error_label ?length:length_outer (py_int_list_as_array ?error_label ?length:length_inner) arr let py_string_list_list_as_array ?error_label ?length_outer ?length_inner arr = py_list_list_as_array_map ?error_label ?length:length_outer (py_string_list_as_array ?error_label ?length:length_inner) arr let py_float_tensor ?(init=(fun _ -> 0.0)) index_ranges = let nr_indices = Array.length index_ranges in let v_indices = Array.make nr_indices 0 in if nr_indices = 0 then (pyfloat_fromdouble (init v_indices), fun _ -> failwith "Cannot set rank-0 python float tensor!") else let rec build pos = let range = index_ranges.(pos) in Py.List.init range (fun ix_here -> let () = v_indices.(pos) <- ix_here in if pos = nr_indices-1 then pyfloat_fromdouble (init v_indices) else build (succ pos)) in let structure = build 0 in let setter indices value = let rec walk sub_structure pos = let i = indices.(pos) in if pos = nr_indices-1 then Py.List.set sub_structure i value else walk (Py.List.get sub_structure i) (succ pos) in walk structure 0 in (structure,setter) let int_array_to_python = Py.List.of_array_map Py.Long.of_int let float_array_to_python = Py.List.of_array_map Py.Float.of_float let register_for_python stuff = let ocaml_module = Py.Import.add_module "ocaml" in let register (python_name, value) = Py.Object.set_attr_string ocaml_module python_name value in Array.iter register stuff let register_pre_functions_for_python stuff = let prepare (python_name, pre_fun) = (python_name, pre_fun python_name) in register_for_python (Array.map prepare stuff) let python_last_value = Py.last_value let pywrap_closure_docstring docstring f = Py.Callable.of_function_as_tuple ~docstring f let pyrefcount = Py.Object.reference_count let pylist_get = Py.List.get let pylist_set = Py.List.set let pylist_fromarray = Py.List.of_array let py_repr obj = Py.Object.string_of_repr obj let pyunwrap_value = Py.Capsule.unsafe_unwrap_value let pywrap_value = Py.Capsule.unsafe_wrap_value type funcptr type funcent = funcptr * int * int * bool type pymodule_func = { pyml_name : string ; pyml_func : (pyobject -> pyobject) ; pyml_flags : int ; pyml_doc : string; } pycaml.mli000066400000000000000000001005271452051003100130420ustar00rootroot00000000000000(** Embedding Python into OCaml. (C) arty 2002 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA A Derivative of Art Yerkes' 2002 Pycaml module. Modifications (C) 2005 Dr. Thomas Fischbacher, Giuliano Bordignon, Dr. Hans Fangohr, SES, University of Southampton More modifications are by Barry Schwartz. Copyright (C) 2009 Barry Schwartz. Adapted for py.ml by Thierry Martinez. Copyright (C) 2016 Thierry Martinez. *) (** {2 Background Information} *) (** The original code is available in Debian as package "pycaml". For various reasons, we hijacked it so that we can easily both fix bugs and extend it. This is permitted by the Pycaml license (the GNU LGPL). Note: the layout and hierarchical structure of the documentation could need some more work. *) (** {3 OCaml Types, Python Types, and general issues of typing} *) (** Python objects are wrapped up within OCaml as entities of type [pyobject]. *) type pyobject = Py.Object.t (** The following types are slightly esoteric; normally, users of this module should not have any need to access them. *) type funcptr type funcent = (funcptr * int * int * bool) type pymodule_func = { pyml_name : string ; pyml_func : (pyobject -> pyobject) ; pyml_flags : int ; pyml_doc : string; } val py_profile_reset: unit -> unit val py_profile_report: unit -> (string * float * float) array (* name * total_time * nr_calls - Note that we use float to count nr_calls to work around fixnum limits! *) val py_activate_profiling: unit -> bool val py_deactivate_profiling: unit -> bool (** As Python is a dynamically typed language, [pyobject] values may represent entities of very different nature. The [pytype] function maps a pyobject to its type, or rather, a selection of types that have been made known to OCaml. The default for "unknown" values is OtherType. Note (for advanced users only): This in particular holds for [PyCObject] values, which are used at present to wrap up OCaml values opaquely within Python values: at present, these appear to be of type [OtherType], but there might be good reason to change this in the future. *) type pyobject_type = | TupleType | BytesType | UnicodeType | BoolType | IntType | FloatType | ListType | NoneType | CallableType | ModuleType | ClassType | TypeType | DictType | NullType | CamlpillType | OtherType | EitherStringType (* Signifies that either of BytesType or UnicodeType is allowed. *) | CamlpillSubtype of string (* Signifies that only the particular Camlpill variety is allowed. *) | AnyType (* Allow any python object. *) type pyerror_type = | Pyerr_Exception | Pyerr_StandardError | Pyerr_ArithmeticError | Pyerr_LookupError | Pyerr_AssertionError | Pyerr_AttributeError | Pyerr_EOFError | Pyerr_EnvironmentError | Pyerr_FloatingPointError | Pyerr_IOError | Pyerr_ImportError | Pyerr_IndexError | Pyerr_KeyError | Pyerr_KeyboardInterrupt | Pyerr_MemoryError | Pyerr_NameError | Pyerr_NotImplementedError | Pyerr_OSError | Pyerr_OverflowError | Pyerr_ReferenceError | Pyerr_RuntimeError | Pyerr_SyntaxError | Pyerr_SystemExit | Pyerr_TypeError | Pyerr_ValueError | Pyerr_ZeroDivisionError exception Pycaml_exn of (pyerror_type * string) val pytype : pyobject -> pyobject_type (** Also note the existence of [pytype_name], which maps python types to human-readable strings. *) (* Pycaml contains quite some stuff which we should not tell the outside world about. For now, this is in comments, but eventually, we should perhaps even remove it from there. *) (** {3 Initialization} *) (** The Python interpreter has to be initialized, which is done via [py_initialize]. Note that this module does call this function automatically when it is initialized itself, so the end user does not have to worry about this. Note that Python initialization seems to be idempotent, so there should not be any problems if one starts up a python interpreter first, and then loads a shared object via Python's foreign function interface which itself initializes OCaml and pycaml. (Note: However, this still needs more testing!) *) val py_initialize : unit -> unit val py_finalize : unit -> unit (** {3 Functions from the original Pycaml} *) (** There is a collection of functions from the original Pycaml module which are not-too-well-documented. For some of them, there are examples available, and often, one can guess what they are supposed to do from their name and type. (Admittedly, this is a quite unsatisfactory state of affairs, but on the other hand, as it turns out, we will have to use only very few of them. So for now, if there is a question, look at the source, or ask [t.fischbacher\@soton.ac.uk].) In order not to clutter the Pycaml documentation with a block of unreadable code, they have been moved to the last section. *) (** {3 On wrapping up Ocaml values for Python} *) (** The Pycaml functions [pywrap_value] and [pyunwrap_value] are elementary low-level primitives to make opaque Python values that hold OCaml values. As Python is a dynamically typed language, and OCaml is a statically typed language, and both achieve safety in a somewhat misaligned way, this interface may be considered as dangerous. In fact, it allows one to break OCaml type safety by mapping a statically typed value to a dynamically typed Python value and back. This means that a Python user handing a wrapped-up ocaml value of a different type than expected over to an OCaml callback may crash the system. This module provides an extension to the original Pycaml which will have added checks that prevent precisely such a situation and therefore is safer. Note however, that at the moment, it is only foolproof if one does not mix this up with other [PyCObject] Python values. (Presumably, it can be tightened up by introducing a new primitive Python type [PyCamlObject]. TODO.) *) val pywrap_value : 'a -> pyobject val pyunwrap_value : pyobject -> 'a (** {2 Genuine Extensions to the original Pycaml} *) (** {3 Converting values and handling errors} *) val py_repr : pyobject -> string val pylist_fromarray : pyobject array -> pyobject val pylist_toarray : pyobject -> pyobject array (** Map an OCaml array of Python values to a Python list and vice versa. (This was just missing.) *) val pylist_set : pyobject -> int -> pyobject -> unit val pylist_get : pyobject -> int -> pyobject val pyrefcount: pyobject -> int val pywrap_closure_docstring : string -> (pyobject -> pyobject) -> pyobject (** While the functions in ocaml.* should not be made visible to end users directly, it may nevertheless be helpful to be able to set docstrings on them. *) (** Return a name-string for an Ocaml Python-Object-Type value. Used mainly for debugging and in error messages. *) val pytype_name: pyobject_type -> string (** Return the last value that was computed interactively at the Python prompt *) val python_last_value: unit -> pyobject val py_true : unit -> pyobject val py_false : unit -> pyobject val py_is_true : pyobject -> bool (** A convenient function to make a collection of [pyobject] values (which usually will be OCaml callbacks) known to Python in one go. The strings give the names under which the corresponding values should appear in Python's "[ocaml]" module, which Pycaml will add to Python and automatically [import] on the Python side. Note that as a convention, one must not register names that start with the string "[example_]" or "[sys_]", as those are reserved for internal use by Pycaml. *) val register_for_python : (string * pyobject) array -> unit val register_pre_functions_for_python : (string * (string ->pyobject)) array -> unit val float_array_to_python : float array -> pyobject val int_array_to_python : int array -> pyobject (** These functions provides a quick and convenient way to pass a simple array of numbers to Python. Note that {i neither} on the OCaml nor on the Python side, the special data structure for efficient manipulation of large numerical arrays is used (OCaml: bigarray, Python: numarray). Rather, this just maps ordinary arrays. *) val py_float_tensor : ?init:(int array -> float) -> int array -> pyobject * (int array -> pyobject -> unit) (** This little helper creates a nested float array python structure that is supposed to represent a multi-indexed tensor, plus a function to set tensor entries. *) (* XXX These have to be documented! *) val py_homogeneous_list_as_array : ?error_label:string -> ?length:int -> string -> (pyobject -> bool) -> (pyobject -> 'a) -> pyobject -> 'a array val py_float_list_as_array : ?error_label:string -> ?length:int -> pyobject -> float array val py_number_list_as_float_array : ?error_label:string -> ?length:int -> pyobject -> float array val py_int_list_as_array : ?error_label:string -> ?length:int -> pyobject -> int array val py_string_list_as_array : ?error_label:string -> ?length:int -> pyobject -> string array val py_list_list_as_array : ?error_label:string -> ?length:int -> pyobject -> pyobject array val py_list_list_as_array2 : ?error_label:string -> ?length:int -> pyobject -> pyobject array array val py_float_list_list_as_array : ?error_label:string -> ?length_outer:int -> ?length_inner:int -> pyobject -> float array array val py_number_list_list_as_float_array : ?error_label:string -> ?length_outer:int -> ?length_inner:int -> pyobject -> float array array val py_int_list_list_as_array : ?error_label:string -> ?length_outer:int -> ?length_inner:int -> pyobject -> int array array val py_string_list_list_as_array : ?error_label:string -> ?length_outer:int -> ?length_inner:int -> pyobject -> string array array val unpythonizing_function : ?name:string -> ?catch_weird_exceptions:bool -> ?extra_guards:(pyobject -> string option) array -> ?expect_tuple:bool -> pyobject_type array -> (pyobject array -> 'a) -> pyobject -> 'a val pythonize_string : string -> pyobject val unpythonize_string : pyobject -> string (** This helper simplifies the creation of OCaml callbacks that can be registered in Python's "[ocaml]" module. First argument: An array of [pyobject_type] Python types. Second argument: A "body" function B mapping an OCaml array of Python values to a Python return value. Optional argument: An array of extra checks to be performed on the arguments, one by one, returning an optional error message. The body function (as well as the optional checks) will be wrapped up in code that first checks for the correct number and the specified Python types of arguments, so B can rely on the n'th entry of its Python argument array being of the Python type specified in the n'th position of the type array. XXX Note: we need examples in the documentation! *) val python_interfaced_function : ?name:string -> ?catch_weird_exceptions:bool -> ?doc:string -> ?extra_guards:(pyobject -> string option) array -> pyobject_type array -> (pyobject array -> pyobject) -> pyobject val python_pre_interfaced_function : ?catch_weird_exceptions:bool -> ?doc:string -> ?extra_guards:(pyobject -> string option) array -> pyobject_type array -> (pyobject array -> pyobject) -> (string -> pyobject) (** Sometimes, we want to manipulate complicated structures via Python which are implemented in OCaml, and about whose interna only OCaml should know and have to worry. So, all that one can do from Python is to place such values in containers (tuples, lists) and retrieve them back, pass them around, and hand them over to OCaml callbacks. In order to ensure type safety, we have to extend OCaml by a primitive dynamic type system for Python-wrapped OCaml values. This is based on the following assumptions: {ol {li The number of different OCaml types we might want to make visible to Python is quite limited. (In particular, we do not even try to properly support polymorphism.)} {li Python should be allowed to take a peek at the type name of a wrapped OCaml value at runtime} } Thus, before one can opaquely wrap up OCaml values in "ocamlpills" for Python, one has to register a type name with Pycaml. From the Python side, the function [ocaml.sys_ocamlpill_type(x)] will map the ocamlpill [x] to the registered type string. *) val register_ocamlpill_types : string array -> unit (** py.ml: Pill types are not required to be registered. This function does nothing and is provided for compatibility only. *) val ocamlpill_type_of : pyobject -> string (** Given an ocamlpill type name (which was registered before using [register_ocamlpill_type]), as well as a witness of the type in question in form of a prototypical OCaml value, make a function that maps other OCaml values of the same type as the prototype to Python ocamlpills. This function is exported to python as [ocaml.sys_ocamlpill_type]. Note: a simple type system hack is used to ensure that the wrapper function generated can only be applied to OCaml values of the proper type. One major drawback of this is that presumably, the prototypical object provided cannot be garbage collected until the wrapper function is. (A clever compiler might be able to figure out how to get rid of that, though.) XXX Provide example code! *) val pill_type_mismatch_exception : ?position:'a -> ?exn_name:string -> string -> string -> exn val check_pill_type : ?position:'a -> ?exn_name:string -> string -> pyobject -> unit val make_ocamlpill_wrapper_unwrapper : string -> 'a -> ('a -> pyobject) * (pyobject -> 'a) (* Deprecated, I guess. Use |make_pill_wrapping| instead. *) (** py.ml: the signature has been changed from [string -> 'a -> ('a -> pyobject) * (pyobject -> 'b)] to [string -> 'a -> ('a -> pyobject) * (pyobject -> 'a)]. The second argument is ignored and the function calls {!Py.Capsule.make}. Applying the function twice to the same type name raises a failure ([Failure _]). *) val make_pill_wrapping : string -> 'a -> ('a -> pyobject) * (pyobject -> 'a) (* A less cumbersome synonym. *) (** py.ml: the signature has been changed from [string -> 'a -> ('a -> pyobject) * (pyobject -> 'b)] to [string -> 'a -> ('a -> pyobject) * (pyobject -> 'a)]. The second argument is ignored and the function calls {!Py.Capsule.make}. Applying the function twice to the same type name raises a failure ([Failure _]). *) (** Also, we want to be able to pass optional arguments from python to OCaml. The convention which we use for now is as follows: - Conceptually, an optional argument has to be a container monadic type. - The only thing offered by python which looks like such a thing is the list. - Hence, optional values are represented as 0-element or 1-element lists on the python side. We then need ocaml functions that make it convenient to handle the automatic unpacking of such values. (XXX Note: we need examples in the documentation that show how to use this!) *) val py_optionally : (pyobject -> 'a) -> pyobject -> 'a option val guarded_pyint_asint: pyobject -> int val guarded_pyfloat_asfloat: pyobject -> float val guarded_pynumber_asfloat: pyobject -> float val guarded_pybytes_asstring: pyobject -> string val guarded_pylist_toarray: pyobject -> pyobject array val guarded_pytuple_toarray: pyobject -> pyobject array val pycallable_asfun : pyobject -> pyobject array -> pyobject (** This is semi-internal - It should only be used for writing other convenience type applicators that have their own way of doing the checking. *) val ocamlpill_hard_unwrap : pyobject -> 'a (** {3 Running and evaluating Python from within OCaml} *) (** This function allows us to set Python's [sys.argv] *) val set_python_argv : string array -> unit (** A convenience function for just letting the Python interpreter evaluate a block of Python code. *) val python_eval : string -> int (** One may use [python_eval "execfile(...)"] to load Python code into the interpreter. This function provides a slightly nicer way to do the same. Note 1: Internally, this uses [python_eval]. The [int] return value is ignored, however. Note 2: As we do not bother to properly escape quotation marks, this will not work as supposed on filenames containing double quotes. (Yes, this is a bug and should better be fixed!) *) val python_load: string -> unit (** Start the interactive python toplevel: *) val python : unit -> int (** Start the ipython toplevel. Note: for still unknown reasons, this does not seem to be 100% reliable, and especially seems to fail in many situations where [Pycaml.ipython()] is called not from the OCaml toplevel. May be some crazy terminal handling bug. Addition: 23/01/2006 fangohr: On Mac OS X, one of the problems is that there are often several Python installations. (One is provided by Apple, but usually a fink or Darwinport installation is actually meant to use.) For fink-python (the binary installed in /sw/bin, it helps to set the shell environment variable PYTHONHOME=/sw . Then the call to ipython works fine. *) val ipython : unit -> int (** {2 Python Functions} *) (** All functions which are made visible from OCaml to python by means of [register_for_python] go into the Python module [ocaml]. Usually, one wants to place low-level interface functions there and build higher levels of abstraction on the python side on top of it which are more convenient (maybe object-oriented) to the Python end user. So, the user of a Python library that uses OCaml callbacks internally should (ideally) never notice the existence of the [ocaml] Python module. The following names are pre-registered in the [ocaml] module. Note that they all start with the reserved prefixes [sys_] or [example_]. - [sys_ocamlpill_type]: Function that maps an OCaml pill to a type string, so that Python can find out what a given pill is supposed to be. (The OCaml function name is [ocamlpill_type_of].) - [sys_python]: Function that starts a recursive Python toplevel. This may seem strange at first, but actually is highly useful e.g. for providing some interactive control deep inside a contrived function during debugging. Return value is the value computed last on the recursive python command prompt. - [example_test_interface]: Function that just prints a test string. - [example_the_answer]: The number "42", put in the [ocaml] module by OCaml. - [example_make_powers]: A function mapping an integer [n] and a float [p] to the array {v [|1.0**p,2.0**p,...,(float_of_int n)**p|] v}. - [example_hypotenuse]: A function mapping two floatingpoint values [x,y] to [sqrt(x**2+y**2)]. It is instructive to have a look at the pycaml source providing the [example_] entries to see how one can publish other constants and functions to Python. *) (** {2 Code Examples} *) (** The implementations of [example_make_powers] and [example_hypotenuse] demonstrate how to use [python_interfaced_function]: {v let _py_make_powers = python_interfaced_function ~extra_guards: [|(fun py_len -> let len = pyint_asint py_len in if len < 0 then Some "Negative Length" else None); (fun _ -> None); (* This check never fails *) |] [|IntType;FloatType|] (fun py_args -> let len = pyint_asint py_args.(0) and pow = pyfloat_asdouble py_args.(1) in float_array_to_python (Array.init len (fun n -> let nn = float_of_int (n+1) in nn**pow))) and _py_hypotenuse_2d = python_interfaced_function [|FloatType;FloatType|] (fun py_args -> let x = pyfloat_asdouble py_args.(0) and y = pyfloat_asdouble py_args.(1) in pyfloat_fromdouble (sqrt(x*.x+.y*.y))) in register_for_python [|("example_make_powers", _py_make_powers); ("example_hypotenuse", _py_hypotenuse_2d); |] ;; v} *) (** {2 Appendix: signatures of undocumented functions from the original Pycaml} *) val pyerr_print : unit -> unit val py_exit : int -> unit val pyerr_printex : int -> unit val py_setprogramname : string -> unit val py_setpythonhome : string -> unit val py_isinitialized : unit -> int val pyrun_simplestring : string -> int val pyrun_anyfile : int * string -> int val pyrun_simplefile : int * string -> int val pyrun_interactiveone : int * string -> int val pyrun_interactiveloop : int * string -> int val py_fdisinteractive : int * string -> int val pyrun_anyfileex : int * string * int -> int val pyrun_simplefileex : int * string * int -> int val py_getprogramname : unit -> string val py_getpythonhome : unit -> string val py_getprogramfullpath : unit -> string val py_getprefix : unit -> string val py_getexecprefix : unit -> string val py_getpath : unit -> string val py_getversion : unit -> string val py_getplatform : unit -> string val py_getcopyright : unit -> string val py_getcompiler : unit -> string val py_getbuildinfo : unit -> string val pyrun_string : string * int * pyobject * pyobject -> pyobject val pyrun_file : int * string * int * pyobject * pyobject -> pyobject val pyrun_fileex : int * string * int * pyobject * pyobject * int -> pyobject val py_compilestring : string * string * int -> pyobject val pyobject_print : pyobject * int * int -> int val pyobject_repr : pyobject -> pyobject val pyobject_str : pyobject -> pyobject val pyobject_unicode : pyobject -> pyobject val pyobject_richcompare : pyobject * pyobject * int -> pyobject val pyobject_getattrstring : pyobject * string -> pyobject val pyobject_getattr : pyobject * pyobject -> pyobject val pyobject_istrue : pyobject -> int val pyobject_not : pyobject -> int val pycallable_check : pyobject -> int val pyobject_hasattr : pyobject * pyobject -> int val pyobject_richcomparebool : pyobject * pyobject * int -> int val pyobject_setattrstring : pyobject * string * pyobject -> int val pyobject_hasattrstring : pyobject * string -> int (* IFDEF PYCAML2 THEN*) val pyobject_compare : pyobject * pyobject -> int (* END*) (* Currently not implemented. val pynumber_coerce : pyobject * pyobject -> (pyobject * pyobject) option val pynumber_coerceex : pyobject * pyobject -> (pyobject * pyobject) option *) val pyobject_setattr : pyobject * pyobject * pyobject -> int val pyobject_hash : pyobject -> int64 val pybytes_size : pyobject -> int val pystring_size : pyobject -> int (* Legacy support *) val pybytes_asstring : pyobject -> string val pystring_asstring : pyobject -> string (* Legacy support *) val pybytes_asstringandsize : pyobject -> string val pystring_asstringandsize : pyobject -> string (* Legacy support *) val pybytes_fromstring : string -> pyobject val pystring_fromstring : string -> pyobject (* Legacy support *) (* IFDEF PYMAJOR2 THEN*) val pybytes_format : pyobject * pyobject -> pyobject val pystring_format : pyobject * pyobject -> pyobject (* Legacy support *) (* END*) val pyunicode_asutf8string : pyobject -> pyobject val pyunicode_asutf16string : pyobject -> pyobject val pyunicode_asutf32string : pyobject -> pyobject val pyunicode_decodeutf8 : (string * string option) -> pyobject val pyunicode_decodeutf16 : (string * string option * int option) -> pyobject val pyunicode_decodeutf32 : (string * string option * int option) -> pyobject val pyunicode_fromunicode : (int -> int) -> int -> pyobject val pyunicode_asunicode : pyobject -> int array val pyunicode_getsize : pyobject -> int val pydict_new : unit -> pyobject val pydict_getitem : pyobject * pyobject -> pyobject val pydict_setitem : pyobject * pyobject * pyobject -> int val pydict_delitem : pyobject * pyobject -> int val pydict_clear : pyobject -> unit (* val pydict_next : pyobject * int -> (pyobject * pyobject * int) option <-- currently not implemented *) val pydict_keys : pyobject -> pyobject val pydict_values : pyobject -> pyobject val pydict_items : pyobject -> pyobject val pydict_copy : pyobject -> pyobject val pydict_size : pyobject -> int val pydict_getitemstring : pyobject * string -> pyobject val pydict_delitemstring : pyobject * string -> int val pydict_setitemstring : pyobject * string * pyobject -> int val pyint_fromlong : int64 -> pyobject val pyint_aslong : pyobject -> int64 (* IFDEF PYMAJOR2 THEN*) val pyint_getmax : unit -> int64 (* END*) val pyfloat_fromdouble : float -> pyobject val pyfloat_asdouble : pyobject -> float val pymodule_new : string -> pyobject val pymodule_getdict : pyobject -> pyobject val pymodule_getname : pyobject -> string val pymodule_getfilename : pyobject -> string val pytuple_new : int -> pyobject val pytuple_size : pyobject -> int val pytuple_getitem : pyobject * int -> pyobject val pytuple_setitem : pyobject * int * pyobject -> int val pytuple_getslice : pyobject * int * int -> pyobject (** py.ml: the result type has been changed from [int] to [pyobject]. *) val pyslice_new : pyobject * pyobject * pyobject -> pyobject (* val pyslice_getindices : pyobject * int -> (int * int * int) option <- Currently not supported *) val pyerr_setnone : pyobject -> unit val pyerr_setobject : pyobject * pyobject -> unit val pyerr_setstring : pyobject * string -> unit val pyerr_occurred : unit -> pyobject val pyerr_clear : unit -> unit val pyerr_fetch : pyobject * pyobject * pyobject -> pyobject * pyobject * pyobject val pyerr_givenexceptionmatches : pyobject * pyobject -> int val pyerr_exceptionmatches : pyobject -> int val pyerr_normalizeexception : pyobject * pyobject * pyobject -> pyobject * pyobject * pyobject (* IFDEF PYMAJOR2 THEN*) val pyclass_new : pyobject * pyobject * pyobject -> pyobject val pyinstance_new : pyobject * pyobject * pyobject -> pyobject val pyinstance_newraw : pyobject * pyobject -> pyobject (* END*) (* IFDEF PYMAJOR2 THEN*) val pymethod_new : pyobject * pyobject * pyobject -> pyobject (* ELSE*) (*val pymethod_new : pyobject * pyobject -> pyobject *) (* END*) val pymethod_function : pyobject -> pyobject val pymethod_self : pyobject -> pyobject (* IFDEF PYMAJOR2 THEN*) val pymethod_class : pyobject -> pyobject (* END*) val pyimport_getmagicnumber : unit -> int64 val pyimport_execcodemodule : pyobject * string -> pyobject val pyimport_execcodemoduleex : string * pyobject * string -> pyobject val pyimport_getmoduledict : unit -> pyobject val pyimport_addmodule : string -> pyobject val pyimport_importmodule : string -> pyobject val pyimport_importmoduleex : string * pyobject * pyobject * pyobject -> pyobject val pyimport_import : pyobject -> pyobject val pyimport_reloadmodule : pyobject -> pyobject val pyimport_cleanup : unit -> unit val pyimport_importfrozenmodule : string -> int val pyeval_callobjectwithkeywords : pyobject * pyobject * pyobject -> pyobject val pyeval_callobject : pyobject * pyobject -> pyobject val pyeval_getbuiltins : unit -> pyobject val pyeval_getglobals : unit -> pyobject val pyeval_getlocals : unit -> pyobject (* val pyeval_getframe : unit -> pyobject -- FIX: see comment in stubs code. *) (* IFDEF PYMAJOR2 THEN*) val pyeval_getrestricted : unit -> int (* END*) val pyobject_type : pyobject -> pyobject val pyobject_size : pyobject -> int val pyobject_getitem : pyobject * pyobject -> pyobject val pyobject_setitem : pyobject * pyobject * pyobject -> int val pyobject_delitem : pyobject * pyobject -> int val pyobject_ascharbuffer : pyobject -> string val pyobject_asreadbuffer : pyobject -> string val pyobject_aswritebuffer : pyobject -> string val pynumber_check : pyobject -> int val pynumber_add : pyobject * pyobject -> pyobject val pynumber_subtract : pyobject * pyobject -> pyobject val pynumber_multiply : pyobject * pyobject -> pyobject val pynumber_truedivide : pyobject * pyobject -> pyobject val pynumber_floordivide : pyobject * pyobject -> pyobject (* IFDEF PYMAJOR2 THEN*) val pynumber_divide : pyobject * pyobject -> pyobject (* END*) val pynumber_remainder : pyobject * pyobject -> pyobject val pynumber_divmod : pyobject * pyobject -> pyobject val pynumber_power : pyobject * pyobject * pyobject -> pyobject val pynumber_negative : pyobject -> pyobject val pynumber_positive : pyobject -> pyobject val pynumber_absolute : pyobject -> pyobject val pynumber_invert : pyobject -> pyobject val pynumber_lshift : pyobject * pyobject -> pyobject val pynumber_rshift : pyobject * pyobject -> pyobject val pynumber_and : pyobject * pyobject -> pyobject val pynumber_xor : pyobject * pyobject -> pyobject val pynumber_or : pyobject * pyobject -> pyobject (* IFDEF PYMAJOR2 THEN*) val pynumber_int : pyobject -> pyobject (* END*) val pynumber_long : pyobject -> pyobject val pynumber_float : pyobject -> pyobject val pynumber_inplaceadd : pyobject * pyobject -> pyobject val pynumber_inplacesubtract : pyobject * pyobject -> pyobject val pynumber_inplacemultiply : pyobject * pyobject -> pyobject val pynumber_inplacetruedivide : pyobject * pyobject -> pyobject val pynumber_inplacefloordivide : pyobject * pyobject -> pyobject (* IFDEF PYMAJOR2 THEN*) val pynumber_inplacedivide : pyobject * pyobject -> pyobject (* END*) val pynumber_inplaceremainder : pyobject * pyobject -> pyobject val pynumber_inplacelshift : pyobject * pyobject -> pyobject val pynumber_inplacershift : pyobject * pyobject -> pyobject val pynumber_inplaceand : pyobject * pyobject -> pyobject val pynumber_inplacexor : pyobject * pyobject -> pyobject val pynumber_inplaceor : pyobject * pyobject -> pyobject val pynumber_inplacepower : pyobject * pyobject * pyobject -> pyobject val pysequence_check : pyobject -> int val pysequence_size : pyobject -> int val pysequence_length : pyobject -> int val pysequence_concat : pyobject * pyobject -> pyobject val pysequence_repeat : pyobject * int -> pyobject val pysequence_getitem : pyobject * int -> pyobject (** py.ml: the result type has been changed from [int] to [pyobject]. *) val pysequence_getslice : pyobject * int * int -> pyobject val pysequence_setitem : pyobject * int * pyobject -> int val pysequence_delitem : pyobject * int -> int (** py.ml: one of the two [pyobject] arguments has been removed. *) val pysequence_setslice : pyobject * int * int * pyobject -> int val pysequence_delslice : pyobject * int * int -> int val pysequence_tuple : pyobject -> pyobject val pysequence_list : pyobject -> pyobject val pysequence_fast : pyobject * string -> pyobject val pysequence_count : pyobject * pyobject -> int val pysequence_contains : pyobject * pyobject -> int val pysequence_in : pyobject * pyobject -> int val pysequence_index : pyobject * pyobject -> int val pysequence_inplaceconcat : pyobject * pyobject -> pyobject val pysequence_inplacerepeat : pyobject * int -> pyobject val pymapping_check : pyobject -> int val pymapping_size : pyobject -> int val pymapping_length : pyobject -> int val pymapping_haskeystring : pyobject * string -> int val pymapping_haskey : pyobject * pyobject -> int val pymapping_getitemstring : pyobject * string -> pyobject val pymapping_setitemstring : pyobject * string * pyobject -> int val pyiter_check : pyobject -> int val pyiter_next : pyobject -> pyobject val pynull : unit -> pyobject val pynone : unit -> pyobject val pytuple_fromarray : pyobject array -> pyobject val pytuple_fromsingle : pyobject -> pyobject val pytuple_empty : pyobject val pytuple2 : pyobject * pyobject -> pyobject val pytuple3 : pyobject * pyobject * pyobject -> pyobject val pytuple4 : pyobject * pyobject * pyobject * pyobject -> pyobject val pytuple5 : pyobject * pyobject * pyobject * pyobject * pyobject -> pyobject val pyint_fromint : int -> pyobject val pyint_asint : pyobject -> int val pytuple_toarray : pyobject -> pyobject array val pywrap_closure : (pyobject -> pyobject) -> pyobject (* val version : unit -> string (* This function returns a unique code version string. *) *) pyml.opam000066400000000000000000000015411452051003100127050ustar00rootroot00000000000000# This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "OCaml bindings for Python" description: "OCaml bindings for Python 2 and Python 3" maintainer: ["Thierry Martinez "] authors: ["Thierry Martinez "] license: "BSD-2-Clause" homepage: "http://github.com/thierry-martinez/pyml" doc: "http://github.com/thierry-martinez/pyml" bug-reports: "http://github.com/thierry-martinez/pyml/issues" depends: [ "dune" {>= "2.8"} "ocaml" {>= "3.12.1"} "ocamlfind" {build} "stdcompat" {>= "18"} "conf-python-3-dev" {with-test} "odoc" {with-doc} ] depopts: ["utop"] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/thierry-martinez/pyml.git" pyml_arch.ml.c000066400000000000000000000006611452051003100136010ustar00rootroot00000000000000#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 pyml_arch.mli000066400000000000000000000001211452051003100135200ustar00rootroot00000000000000type t = Windows | Mac | Unix val os : t val fd_of_int: int -> Unix.file_descr pyml_stubs.c000066400000000000000000001206351452051003100134210ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 wchar_t *mode); static void *xmalloc(size_t size) { void *p = malloc(size); if (!p) { caml_failwith("Virtual memory exhausted\n"); } return p; } #ifdef _WIN32 #include typedef HINSTANCE library_t; static library_t open_library(const char *filename) { return LoadLibrary(filename); } static char * get_library_error() { return "Unable to load library"; } static void close_library(library_t library) { if (!FreeLibrary(library)) { fprintf(stderr, "close_library.\n"); exit(EXIT_FAILURE); } } static library_t get_default_library(void) { return GetModuleHandle(0); } static void * find_symbol(library_t library, const char *name) { return GetProcAddress(library, name); } int unsetenv(const char *name) { size_t len = strlen(name); char *string = xmalloc(len + 2); int result; snprintf(string, len + 2, "%s=", name); result = _putenv(string); free(string); return result; } extern int win_CRT_fd_of_filedescr(value handle); static FILE * file_of_file_descr(value file_descr, const char *mode) { CAMLparam1(file_descr); int fd = win_CRT_fd_of_filedescr(file_descr); FILE *result = _fdopen(_dup(fd), mode); CAMLreturnT(FILE *, result); } #else #include typedef void *library_t; static library_t open_library(const char *filename) { return dlopen(filename, RTLD_LAZY | RTLD_GLOBAL); } static char * get_library_error() { return dlerror(); } void close_library(library_t filename) { if (dlclose(filename)) { fprintf(stderr, "close_library: %s.\n", dlerror()); exit(EXIT_FAILURE); } } static library_t get_default_library(void) { return RTLD_DEFAULT; } static void * find_symbol(library_t library, const char *name) { return dlsym(library, name); } static FILE * file_of_file_descr(value file_descr, const char *mode) { CAMLparam1(file_descr); int fd = Int_val(file_descr); FILE *result = fdopen(dup(fd), mode); CAMLreturnT(FILE *, result); } #endif /* Global variables for the library */ /* version_major != 0 iff the library is initialized */ static int version_major; static int version_minor; static library_t library; /* Functions that are special enough to deserved to be wrapped specifically */ /* Wrapped by pywrap_closure */ static PyObject *(*Python_PyCFunction_NewEx) (PyMethodDef *, PyObject *, PyObject *); /* Wrapped by closure and capsule */ static void *(*Python27_PyCapsule_New) (void *, const char *, PyCapsule_Destructor); static void *(*Python27_PyCapsule_GetPointer)(PyObject *, const char *); static int (*Python27_PyCapsule_IsValid)(PyObject *, const char *); static void *(*Python2_PyCObject_FromVoidPtr)(void *, void (*)(void *)); static void *(*Python2_PyCObject_AsVoidPtr)(PyObject *); /* Hack for multi-arguments */ static PyObject *(*Python_PyObject_CallFunctionObjArgs)(PyObject *, ...); static PyObject *(*Python_PyObject_CallMethodObjArgs)( PyObject *, PyObject *, ...); /* Wrapped by PyErr_Fetch_wrapper */ static void (*Python_PyErr_Fetch)(PyObject **, PyObject **, PyObject **); static void (*Python_PyErr_Restore)(PyObject *, PyObject *, PyObject *); static void (*Python_PyErr_NormalizeException) (PyObject **, PyObject **, PyObject **); /* Resolved differently between Python 2 and Python 3 */ static PyObject *Python__Py_FalseStruct; /* Buffer and size */ static int (*Python_PyString_AsStringAndSize) (PyObject *, char **, Py_ssize_t *); static int (*Python_PyObject_AsCharBuffer) (PyObject *, const char **, Py_ssize_t *); static int (*Python_PyObject_AsReadBuffer) (PyObject *, const void **, Py_ssize_t *); static int (*Python_PyObject_AsWriteBuffer) (PyObject *, void **, Py_ssize_t *); /* Length argument */ static PyObject *(*Python_PyLong_FromString)(const char *, const char **, int); /* Internal use only */ static void (*Python_PyMem_Free)(void *); /* Generate traceback objects. */ static PyObject *(*Python_PyThreadState_Get)(); static PyObject *(*Python_PyFrame_New)(PyObject*, PyObject*, PyObject*, PyObject*); static PyObject *(*Python_PyCode_NewEmpty)(const char*, const char*, int); static enum UCS { UCS_NONE, UCS2, UCS4 } ucs; /* Single instance of () */ static PyObject *tuple_empty; #include "pyml.h" #include static void *getcustom( value v ) { return *((void **)Data_custom_val(v)); } static void pydecref( value v ) { if (getcustom(v)) { Py_DECREF((PyObject *)getcustom(v)); } } static int rich_compare_bool_nofail (PyObject *o1, PyObject *o2, int opid) { int result = Python_PyObject_RichCompareBool(o1, o2, opid); if (result == -1) { Python_PyErr_Clear(); result = 0; } return result; } static int pycompare(value v1, value v2) { int result; PyObject *o1 = getcustom(v1); PyObject *o2 = getcustom(v2); if (o1 && !o2) result = -1; else if (o2 && !o1) result = 1; else if (!o1 && !o2) result = 0; else if (version_major < 3) Python2_PyObject_Cmp(o1, o2, &result); else if (rich_compare_bool_nofail(o1, o2, Py_EQ)) result = 0; else if (rich_compare_bool_nofail(o1, o2, Py_LT)) result = -1; else if (rich_compare_bool_nofail(o1, o2, Py_GT)) result = 1; else result = -1; return result; } static intnat pyhash( value v ) { if (getcustom(v)) { intnat result = Python_PyObject_Hash((PyObject *)getcustom(v)); if (result == -1) { Python_PyErr_Clear(); } return result; } else { return 0; } } 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) { 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 = { "PythonObject", pydecref, pycompare, pyhash, pyserialize, pydeserialize }; enum code { CODE_NULL, CODE_NONE, CODE_TRUE, CODE_FALSE, CODE_TUPLE_EMPTY }; static void * resolve(const char *symbol) { void *result = find_symbol(library, symbol); if (!result) { char *fmt = "Cannot resolve %s.\n"; ssize_t size = snprintf(NULL, 0, fmt, symbol); char *msg = xmalloc(size + 1); snprintf(msg, size + 1, fmt, symbol); caml_failwith(msg); } return result; } static void * resolve_optional(const char *symbol) { return find_symbol(library, symbol); } value pyml_wrap(PyObject *object, bool steal) { CAMLparam0(); CAMLlocal1(v); if (!object) { CAMLreturn(Val_int(CODE_NULL)); } if (object == Python__Py_NoneStruct) { CAMLreturn(Val_int(CODE_NONE)); } if (object == Python__Py_TrueStruct) { CAMLreturn(Val_int(CODE_TRUE)); } if (object == Python__Py_FalseStruct) { CAMLreturn(Val_int(CODE_FALSE)); } unsigned long flags = ((struct _typeobject *) pyobjectdescr(pyobjectdescr(object)->ob_type)) ->tp_flags; if (flags & Py_TPFLAGS_TUPLE_SUBCLASS && Python_PySequence_Length(object) == 0) { CAMLreturn(Val_int(CODE_TUPLE_EMPTY)); } if (!steal) { Py_INCREF(object); } v = caml_alloc_custom(&pyops, sizeof(PyObject *), 100, 30000000); *((PyObject **)Data_custom_val(v)) = object; CAMLreturn(v); } PyObject * pyml_unwrap(value v) { if (Is_long(v)) switch (Int_val(v)) { case CODE_NULL: return NULL; case CODE_NONE: return Python__Py_NoneStruct; case CODE_TRUE: return Python__Py_TrueStruct; case CODE_FALSE: return Python__Py_FalseStruct; case CODE_TUPLE_EMPTY: return tuple_empty; } return *((PyObject **)Data_custom_val(v)); } /* static value pyml_wrap_compilerflags(PyCompilerFlags *flags) { CAMLparam0(); CAMLlocal2(ref, some); if (!flags) { CAMLreturn(Val_int(0)); } else { ref = caml_alloc(0, 1); Store_field(ref, 0, Val_int(flags->cf_flags)); some = caml_alloc(0, 1); Store_field(some, 0, ref); CAMLreturn(some); } } */ static PyCompilerFlags * pyml_unwrap_compilerflags(value v) { CAMLparam1(v); 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 { CAMLreturnT(PyCompilerFlags *, NULL); } } /* static value pyml_wrap_intref(int v) { CAMLparam0(); CAMLlocal1(ref); ref = caml_alloc(0, 1); Store_field(ref, 0, Val_int(v)); CAMLreturn(ref); } */ static int pyml_unwrap_intref(value v) { CAMLparam1(v); CAMLreturnT(int, Int_val(Field(v, 0))); } static void * unwrap_capsule(PyObject *obj, const char *type) { if (Python27_PyCapsule_GetPointer) { return Python27_PyCapsule_GetPointer(obj, type); } else { return Python2_PyCObject_AsVoidPtr(obj); } } static PyObject * wrap_capsule(void *ptr, char *type, void (*destr)(PyObject *)) { if (Python27_PyCapsule_New) { return Python27_PyCapsule_New(ptr, type, destr); } else { return Python2_PyCObject_FromVoidPtr(ptr, (void(*)(void *))destr); } } static PyObject * pycall_callback(PyObject *obj, PyObject *args) { CAMLparam0(); CAMLlocal3(ml_out, ml_func, ml_args); PyObject *out; void *p = unwrap_capsule(obj, "ocaml-closure"); if (!p) { Py_INCREF(Python__Py_NoneStruct); CAMLreturnT(PyObject *, Python__Py_NoneStruct); } ml_func = *(value *) p; ml_args = pyml_wrap(args, false); ml_out = caml_callback(ml_func, ml_args); out = pyml_unwrap(ml_out); Py_XINCREF(out); CAMLreturnT(PyObject *, out); } static PyObject * pycall_callback_with_keywords(PyObject *obj, PyObject *args, PyObject *keywords) { CAMLparam0(); CAMLlocal4(ml_out, ml_func, ml_args, ml_keywords); PyObject *out; void *p = unwrap_capsule(obj, "ocaml-closure"); if (!p) { Py_INCREF(Python__Py_NoneStruct); CAMLreturnT(PyObject *, Python__Py_NoneStruct); } ml_func = *(value *) p; ml_args = pyml_wrap(args, false); ml_keywords = pyml_wrap(keywords, false); ml_out = caml_callback2(ml_func, ml_args, ml_keywords); out = pyml_unwrap(ml_out); Py_XINCREF(out); CAMLreturnT(PyObject *, out); } static void caml_destructor(PyObject *v, const char *capsule_name) { value *valptr = (value *) unwrap_capsule(v, capsule_name); caml_remove_global_root(valptr); free(valptr); } static void camldestr_capsule(PyObject *v) { caml_destructor(v, "ocaml-capsule"); } static PyObject * camlwrap_capsule(value val, void *aux_str, int size) { value *v = (value *) malloc(sizeof(value) + size); *v = val; memcpy((char *)v + sizeof(value), aux_str, size); caml_register_global_root(v); return wrap_capsule(v, "ocaml-capsule", camldestr_capsule); } static void * caml_aux(PyObject *obj) { value *v = (value *) unwrap_capsule(obj, "ocaml-closure"); return (char *) v + sizeof(value); } void pyml_assert_python2() { if (version_major != 2) { pyml_assert_initialized(); caml_failwith("Python 2 needed"); } } void pyml_assert_ucs2() { if (ucs != UCS2) { pyml_assert_initialized(); caml_failwith("Python with UCS2 needed"); } } void pyml_assert_ucs4() { if (ucs != UCS4) { pyml_assert_initialized(); caml_failwith("Python with UCS4 needed"); } } void pyml_assert_python3() { if (version_major != 3) { pyml_assert_initialized(); caml_failwith("Python 3 needed"); } } void pyml_check_symbol_available(void *symbol, char *symbol_name) { if (!symbol) { char *fmt = "Symbol unavailable with this version of Python: %s.\n"; ssize_t size = snprintf(NULL, 0, fmt, symbol_name); if (size < 0) { 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) { caml_failwith("Symbol unavailable with this version of Python.\n"); return; } caml_failwith(msg); } } void * deref_not_null(void *pointer) { if (pointer) { return *(void **) pointer; } else { return NULL; } } struct pyml_closure { value value; PyMethodDef method; }; static char *anon_closure = "anonymous_closure"; static void camldestr_closure(PyObject *v) { struct pyml_closure *valptr = unwrap_capsule(v, "ocaml-closure"); const char *ml_doc = valptr->method.ml_doc; const char *ml_name = valptr->method.ml_name; caml_remove_global_root((value *)valptr); free(valptr); free((void *) ml_doc); if (ml_name != anon_closure) free((void *) ml_name); } CAMLprim value pyml_wrap_closure(value name, value docstring, value closure) { CAMLparam3(name, docstring, closure); pyml_assert_initialized(); PyMethodDef ml; PyObject *obj; PyMethodDef *ml_def; ml.ml_name = anon_closure; if (name != Val_int(0)) { ml.ml_name = strdup(String_val(Field(name, 0))); } if (Tag_val(closure) == 0) { ml.ml_flags = 1; ml.ml_meth = pycall_callback; } else { ml.ml_flags = 3; ml.ml_meth = (PyCFunction) pycall_callback_with_keywords; } ml.ml_doc = strdup(String_val(docstring)); struct pyml_closure *v = malloc(sizeof(struct pyml_closure)); v->value = Field(closure, 0); v->method = ml; caml_register_global_root(&v->value); obj = wrap_capsule(v, "ocaml-closure", camldestr_closure); ml_def = (PyMethodDef *) caml_aux(obj); PyObject *f = Python_PyCFunction_NewEx(ml_def, obj, NULL); Py_DECREF(obj); CAMLreturn(pyml_wrap(f, true)); } 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, strlen(py_debug_str)); } else { py_debug = Python2_PyString_FromStringAndSize(py_debug_str, strlen(py_debug_str)); } assert(py_debug); args = singleton(py_debug); debug_build_py = Python_PyObject_Call(get_config_var, args, NULL); if (!debug_build_py) { Python_PyErr_Print(); caml_failwith("Cannot check for debug build"); } 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) { CAMLparam2(filename_ocaml, debug_build_ocaml); if (Is_block(filename_ocaml)) { const char *filename = String_val(Field(filename_ocaml, 0)); library = open_library(filename); if (!library) { caml_failwith(get_library_error()); } } else { library = get_default_library(); } Python_Py_GetVersion = find_symbol(library, "Py_GetVersion"); if (!Python_Py_GetVersion) { caml_failwith("No Python symbol"); } const char *version = Python_Py_GetVersion(); version_major = version[0] - '0'; version_minor = version[2] - '0'; Python_PyCFunction_NewEx = resolve("PyCFunction_NewEx"); if ((version_major == 2 && version_minor >= 7) || version_major >= 3) { Python27_PyCapsule_New = resolve("PyCapsule_New"); Python27_PyCapsule_GetPointer = resolve("PyCapsule_GetPointer"); Python27_PyCapsule_IsValid = resolve("PyCapsule_IsValid"); } Python_PyObject_CallFunctionObjArgs = resolve("PyObject_CallFunctionObjArgs"); Python_PyObject_CallMethodObjArgs = resolve("PyObject_CallMethodObjArgs"); Python_PyErr_Fetch = resolve("PyErr_Fetch"); Python_PyErr_Restore = resolve("PyErr_Restore"); Python_PyErr_NormalizeException = resolve("PyErr_NormalizeException"); Python_PyObject_AsCharBuffer = resolve_optional("PyObject_AsCharBuffer"); Python_PyObject_AsReadBuffer = resolve_optional("PyObject_AsReadBuffer"); Python_PyObject_AsWriteBuffer = resolve_optional("PyObject_AsWriteBuffer"); if (version_major >= 3) { Python__Py_FalseStruct = resolve("_Py_FalseStruct"); Python_PyString_AsStringAndSize = resolve("PyBytes_AsStringAndSize"); } else { Python__Py_FalseStruct = resolve("_Py_ZeroStruct"); Python_PyString_AsStringAndSize = resolve("PyString_AsStringAndSize"); } Python_PyLong_FromString = resolve("PyLong_FromString"); Python_PyMem_Free = resolve("PyMem_Free"); Python_PyThreadState_Get = resolve("PyThreadState_Get"); Python_PyFrame_New = resolve("PyFrame_New"); Python_PyCode_NewEmpty = resolve("PyCode_NewEmpty"); if (version_major >= 3) { Python__Py_wfopen = resolve_optional("_Py_wfopen"); /* Python >=3.10 */ Python__Py_fopen = resolve_optional("_Py_fopen"); } else { Python2_PyCObject_FromVoidPtr = resolve("PyCObject_FromVoidPtr"); Python2_PyCObject_AsVoidPtr = resolve("PyCObject_AsVoidPtr"); } if (find_symbol(library, "PyUnicodeUCS2_AsEncodedString")) { ucs = UCS2; } else if (find_symbol(library, "PyUnicodeUCS4_AsEncodedString")) { ucs = UCS4; } else { ucs = UCS_NONE; } #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 { guess_debug_build(); } tuple_empty = Python_PyTuple_New(0); caml_register_custom_operations(&pyops); CAMLreturn(Val_unit); } struct PyObjectDebug { PyObject *_ob_next; \ PyObject *_ob_prev; PyObjectDescr descr; }; PyObjectDescr *pyobjectdescr(PyObject *obj) { if (trace_refs_build) { return &((struct PyObjectDebug *) obj)->descr; } else { return (PyObjectDescr *) obj; } } CAMLprim value py_is_debug_build() { CAMLparam0(); CAMLreturn(Val_int(debug_build)); } CAMLprim value py_finalize_library(value unit) { CAMLparam1(unit); pyml_assert_initialized(); Py_DECREF(tuple_empty); if (library != get_default_library()) { close_library(library); } version_major = 0; ucs = UCS_NONE; CAMLreturn(Val_unit); } CAMLprim value py_unsetenv(value name_ocaml) { CAMLparam1(name_ocaml); const char *name = String_val(name_ocaml); if (unsetenv(name) == -1) { caml_failwith(strerror(errno)); } CAMLreturn(Val_unit); } CAMLprim value py_get_UCS(value unit) { CAMLparam1(unit); pyml_assert_initialized(); CAMLreturn(Val_int(ucs)); } CAMLprim value PyNull_wrapper(value unit) { CAMLparam1(unit); CAMLreturn(Val_int(CODE_NULL)); } CAMLprim value PyNone_wrapper(value unit) { CAMLparam1(unit); CAMLreturn(Val_int(CODE_NONE)); } CAMLprim value PyTrue_wrapper(value unit) { CAMLparam1(unit); CAMLreturn(Val_int(CODE_TRUE)); } CAMLprim value PyFalse_wrapper(value unit) { CAMLparam1(unit); CAMLreturn(Val_int(CODE_FALSE)); } CAMLprim value PyTuple_Empty_wrapper(value unit) { CAMLparam1(unit); CAMLreturn(Val_int(CODE_TUPLE_EMPTY)); } enum pytype_labels { PyUnknown, Bool, Bytes, Callable, Capsule, Closure, Dict, Float, List, Int, Long, Module, NoneType, Null, Tuple, Type, Unicode, Iter, Set }; static bool is_iterable(PyObject *obj) { PyObject *iter = Python_PyObject_GetIter(obj); if (iter) { Py_DECREF(iter); return true; } else { Python_PyErr_Clear(); return false; } } CAMLprim value pytype(value object_ocaml) { CAMLparam1(object_ocaml); pyml_assert_initialized(); PyObject *object = pyml_unwrap(object_ocaml); if (!object) { CAMLreturn(Val_int(Null)); } PyObject *ob_type = pyobjectdescr(object)->ob_type; struct _typeobject *typeobj = (struct _typeobject *) pyobjectdescr(ob_type); unsigned long flags = typeobj->tp_flags; int result; if (ob_type == Python_PyBool_Type) { result = Bool; } else if (flags & Py_TPFLAGS_BYTES_SUBCLASS) { result = Bytes; } else if (Python_PyCallable_Check(object)) { result = Callable; } else if (Python27_PyCapsule_IsValid && Python27_PyCapsule_IsValid(object, "ocaml-capsule")) { result = Capsule; } else if (Python27_PyCapsule_IsValid && Python27_PyCapsule_IsValid(object, "ocaml-closure")) { result = Closure; } else if (flags & Py_TPFLAGS_DICT_SUBCLASS) { result = Dict; } else if (ob_type == Python_PyFloat_Type || Python_PyType_IsSubtype(ob_type, Python_PyFloat_Type)) { result = Float; } else if (flags & Py_TPFLAGS_LIST_SUBCLASS) { result = List; } else if (flags & Py_TPFLAGS_INT_SUBCLASS) { result = Int; } else if (flags & Py_TPFLAGS_LONG_SUBCLASS) { result = Long; } else if (ob_type == Python_PyModule_Type || Python_PyType_IsSubtype(ob_type, Python_PyModule_Type)) { result = Module; } else if (object == Python__Py_NoneStruct) { result = NoneType; } else if (flags & Py_TPFLAGS_TUPLE_SUBCLASS) { result = Tuple; } else if (flags & Py_TPFLAGS_TYPE_SUBCLASS) { result = Type; } else if (flags & Py_TPFLAGS_UNICODE_SUBCLASS) { result = Unicode; } else if (ob_type == Python_PySet_Type) { result = Set; } else if (is_iterable(object)) { result = Iter; } else { result = PyUnknown; } CAMLreturn(Val_int(result)); } CAMLprim value PyObject_CallFunctionObjArgs_wrapper( value callable_ocaml, value arguments_ocaml) { CAMLparam2(callable_ocaml, arguments_ocaml); pyml_assert_initialized(); PyObject *callable = pyml_unwrap(callable_ocaml); PyObject *result; mlsize_t argument_count = Wosize_val(arguments_ocaml); switch (argument_count) { case 0: result = Python_PyObject_CallFunctionObjArgs(callable, NULL); break; case 1: result = Python_PyObject_CallFunctionObjArgs (callable, pyml_unwrap(Field(arguments_ocaml, 0)), NULL); break; case 2: result = Python_PyObject_CallFunctionObjArgs (callable, pyml_unwrap(Field(arguments_ocaml, 0)), pyml_unwrap(Field(arguments_ocaml, 1)), NULL); break; case 3: result = Python_PyObject_CallFunctionObjArgs (callable, pyml_unwrap(Field(arguments_ocaml, 0)), pyml_unwrap(Field(arguments_ocaml, 1)), pyml_unwrap(Field(arguments_ocaml, 2)), NULL); break; case 4: result = Python_PyObject_CallFunctionObjArgs (callable, pyml_unwrap(Field(arguments_ocaml, 0)), pyml_unwrap(Field(arguments_ocaml, 1)), pyml_unwrap(Field(arguments_ocaml, 2)), pyml_unwrap(Field(arguments_ocaml, 3)), NULL); break; case 5: result = Python_PyObject_CallFunctionObjArgs (callable, pyml_unwrap(Field(arguments_ocaml, 0)), pyml_unwrap(Field(arguments_ocaml, 1)), pyml_unwrap(Field(arguments_ocaml, 2)), pyml_unwrap(Field(arguments_ocaml, 3)), pyml_unwrap(Field(arguments_ocaml, 4)), NULL); break; default: fprintf(stderr, "PyObject_CallFunctionObjArgs_wrapper not implemented for more " "than 5 arguments\n"); exit(EXIT_FAILURE); } CAMLreturn(pyml_wrap(result, true)); } CAMLprim value PyObject_CallMethodObjArgs_wrapper( value object_ocaml, value name_ocaml, value arguments_ocaml) { CAMLparam3(object_ocaml, name_ocaml, arguments_ocaml); pyml_assert_initialized(); PyObject *object = pyml_unwrap(object_ocaml); PyObject *name = pyml_unwrap(name_ocaml); PyObject *result; mlsize_t argument_count = Wosize_val(arguments_ocaml); switch (argument_count) { case 0: result = Python_PyObject_CallMethodObjArgs(object, name, NULL); break; case 1: result = Python_PyObject_CallMethodObjArgs (object, name, pyml_unwrap(Field(arguments_ocaml, 0)), NULL); break; case 2: result = Python_PyObject_CallMethodObjArgs (object, name, pyml_unwrap(Field(arguments_ocaml, 0)), pyml_unwrap(Field(arguments_ocaml, 1)), NULL); break; case 3: result = Python_PyObject_CallMethodObjArgs (object, name, pyml_unwrap(Field(arguments_ocaml, 0)), pyml_unwrap(Field(arguments_ocaml, 1)), pyml_unwrap(Field(arguments_ocaml, 2)), NULL); break; case 4: result = Python_PyObject_CallMethodObjArgs (object, name, pyml_unwrap(Field(arguments_ocaml, 0)), pyml_unwrap(Field(arguments_ocaml, 1)), pyml_unwrap(Field(arguments_ocaml, 2)), pyml_unwrap(Field(arguments_ocaml, 3)), NULL); break; case 5: result = Python_PyObject_CallMethodObjArgs (object, name, pyml_unwrap(Field(arguments_ocaml, 0)), pyml_unwrap(Field(arguments_ocaml, 1)), pyml_unwrap(Field(arguments_ocaml, 2)), pyml_unwrap(Field(arguments_ocaml, 3)), pyml_unwrap(Field(arguments_ocaml, 4)), NULL); break; default: fprintf(stderr, "PyObject_CallMethodObjArgs_wrapper not implemented for more " "than 5 arguments\n"); exit(EXIT_FAILURE); } CAMLreturn(pyml_wrap(result, true)); } CAMLprim value pyml_capsule_check(value v) { CAMLparam1(v); pyml_assert_initialized(); 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)); } CAMLprim value pyml_wrap_value(value v) { CAMLparam1(v); pyml_assert_initialized(); PyObject *result = camlwrap_capsule(v, NULL, 0); CAMLreturn(pyml_wrap(result, true)); } CAMLprim value pyml_unwrap_value(value x_ocaml) { CAMLparam1(x_ocaml); CAMLlocal1(v); pyml_assert_initialized(); PyObject *x = pyml_unwrap(x_ocaml); void *p = unwrap_capsule(x, "ocaml-capsule"); if (!p) { fprintf(stderr, "pyml_unwrap_value: type mismatch"); exit(EXIT_FAILURE); } v = *(value *) p; CAMLreturn(v); } CAMLprim value PyErr_Fetch_wrapper(value unit) { CAMLparam1(unit); CAMLlocal1(result); pyml_assert_initialized(); PyObject *excType, *excValue, *excTraceback; Python_PyErr_Fetch(&excType, &excValue, &excTraceback); Python_PyErr_NormalizeException(&excType, &excValue, &excTraceback); result = caml_alloc_tuple(3); Store_field(result, 0, pyml_wrap(excType, false)); Store_field(result, 1, pyml_wrap(excValue, false)); Store_field(result, 2, pyml_wrap(excTraceback, false)); CAMLreturn(result); } // PyErr_Restore steals the references. // https://docs.python.org/3/c-api/exceptions.html#c.PyErr_Restore // However the objects can be null, so we do not want to run Py_INCREF if // this is the case as this would trigger some segfaults. CAMLprim value PyErr_Restore_wrapper(value arg0_ocaml, value arg1_ocaml, value arg2_ocaml) { CAMLparam3(arg0_ocaml, arg1_ocaml, arg2_ocaml); pyml_assert_initialized(); PyObject *arg0 = pyml_unwrap(arg0_ocaml); if (arg0) Py_INCREF(arg0); PyObject *arg1 = pyml_unwrap(arg1_ocaml); if (arg1) Py_INCREF(arg1); PyObject *arg2 = pyml_unwrap(arg2_ocaml); if (arg2) Py_INCREF(arg2); Python_PyErr_Restore(arg0, arg1, arg2); CAMLreturn(Val_unit); } CAMLprim value pyml_wrap_string_option(const char *s) { CAMLparam0(); CAMLlocal1(result); if (!s) { CAMLreturn(Val_int(0)); } result = caml_alloc_tuple(1); Store_field(result, 0, caml_copy_string(s)); CAMLreturn(result); } CAMLprim value pyrefcount(value pyobj) { CAMLparam1(pyobj); PyObject *obj = pyml_unwrap(pyobj); CAMLreturn(Val_int(pyobjectdescr(obj)->ob_refcnt)); } static value pyml_wrap_wide_string(wchar_t *ws) { CAMLparam0(); CAMLlocal1(result); size_t n = wcstombs(NULL, ws, 0); if (n == (size_t) -1) { fprintf(stderr, "pyml_wrap_wide_string failure.\n"); exit(EXIT_FAILURE); } char *s = xmalloc((n + 1) * sizeof (char)); wcstombs(s, ws, n); result = caml_copy_string(s); free(s); CAMLreturn(result); } static wchar_t * wide_string_of_string(const char *s) { size_t n = mbstowcs(NULL, s, 0); if (n == (size_t) -1) { fprintf(stderr, "wide_string_of_string failure.\n"); exit(EXIT_FAILURE); } wchar_t *ws = xmalloc((n + 1) * sizeof (wchar_t)); mbstowcs(ws, s, n + 1); return ws; } static wchar_t * pyml_unwrap_wide_string(value string_ocaml) { CAMLparam1(string_ocaml); wchar_t *ws = wide_string_of_string(String_val(string_ocaml)); CAMLreturnT(wchar_t *, ws); } static int16_t * pyml_unwrap_ucs2(value array_ocaml) { CAMLparam1(array_ocaml); mlsize_t len = Wosize_val(array_ocaml); int16_t *result = xmalloc(len * sizeof(int16_t)); size_t i; for (i = 0; i < len; i++) { result[i] = Field(array_ocaml, i); } CAMLreturnT(int16_t *, result); } static int32_t * pyml_unwrap_ucs4(value array_ocaml) { CAMLparam1(array_ocaml); mlsize_t len = Wosize_val(array_ocaml); int32_t *result = xmalloc(len * sizeof(int32_t)); size_t i; for (i = 0; i < len; i++) { result[i] = Field(array_ocaml, i); } CAMLreturnT(int32_t *, result); } static value pyml_wrap_ucs2_option(int16_t *buffer) { CAMLparam0(); CAMLlocal2(result, array); mlsize_t len; if (buffer == NULL) { CAMLreturn(Val_int(0)); } len = 0; while (buffer[len]) { len++; } array = caml_alloc_tuple(len); size_t i; for (i = 0; i < len; i++) { Store_field(array, i, buffer[i]); } result = caml_alloc_tuple(1); Store_field(result, 0, array); CAMLreturn(result); } static value pyml_wrap_ucs4_option_and_free(int32_t *buffer, bool free) { CAMLparam0(); CAMLlocal2(result, array); mlsize_t len; if (buffer == NULL) { CAMLreturn(Val_int(0)); } len = 0; while (buffer[len]) { len++; } array = caml_alloc_tuple(len); size_t i; for (i = 0; i < len; i++) { Store_field(array, i, buffer[i]); } result = caml_alloc_tuple(1); Store_field(result, 0, array); if (free) { Python_PyMem_Free(buffer); } CAMLreturn(result); } #define StringAndSize_wrapper(func, byte_type) \ CAMLprim value \ func##_wrapper(value arg_ocaml) \ { \ CAMLparam1(arg_ocaml); \ CAMLlocal2(result, string); \ PyObject *arg = pyml_unwrap(arg_ocaml); \ byte_type *buffer; \ Py_ssize_t length; \ int return_value; \ return_value = Python_##func(arg, &buffer, &length); \ if (return_value == -1) { \ CAMLreturn(Val_int(0)); \ } \ string = caml_alloc_initialized_string(length, buffer); \ result = caml_alloc_tuple(1); \ Store_field(result, 0, string); \ CAMLreturn(result); \ } StringAndSize_wrapper(PyString_AsStringAndSize, char); StringAndSize_wrapper(PyObject_AsCharBuffer, const char); StringAndSize_wrapper(PyObject_AsReadBuffer, const void); StringAndSize_wrapper(PyObject_AsWriteBuffer, void); static FILE * open_file(value file, const char *mode) { CAMLparam1(file); FILE *result; if (Tag_val(file) == 0) { const char *filename = String_val(Field(file, 0)); if (Python__Py_fopen != NULL) { result = Python__Py_fopen(filename, mode); } else if (Python__Py_wfopen != NULL) { wchar_t *wide_filename = wide_string_of_string(filename); wchar_t *wide_mode = wide_string_of_string(mode); result = Python__Py_wfopen(wide_filename, wide_mode); free(wide_mode); free(wide_filename); } else { result = fopen(filename, mode); } } else { result = file_of_file_descr(Field(file, 0), mode); } CAMLreturnT(FILE *, result); } static void close_file(value file, FILE *file_struct) { CAMLparam1(file); fclose(file_struct); CAMLreturn0; } /* Numpy */ void ** pyml_get_pyarray_api(PyObject *c_api) { if (version_major >= 3) { return (void **)Python27_PyCapsule_GetPointer(c_api, NULL); } else { return (void **)Python2_PyCObject_AsVoidPtr(c_api); } } CAMLprim value get_pyarray_type(value numpy_api_ocaml) { CAMLparam1(numpy_api_ocaml); PyObject *c_api = pyml_unwrap(numpy_api_ocaml); void **PyArray_API = pyml_get_pyarray_api(c_api); PyObject *result = PyArray_API[2]; CAMLreturn(pyml_wrap(result, false)); } CAMLprim value pyarray_of_floatarray_wrapper( value numpy_api_ocaml, value array_type_ocaml, value array_ocaml) { CAMLparam3(numpy_api_ocaml, array_type_ocaml, array_ocaml); pyml_assert_initialized(); PyObject *c_api = pyml_unwrap(numpy_api_ocaml); void **PyArray_API = pyml_get_pyarray_api(c_api); PyObject *(*PyArray_New) (PyTypeObject *, int, npy_intp *, int, npy_intp *, void *, int, int, PyObject *) = PyArray_API[93]; npy_intp length = Wosize_val(array_ocaml); #ifndef ARCH_SIXTYFOUR length /= 2; #endif void *data = (double *) array_ocaml; PyTypeObject (*PyArray_SubType) = (PyTypeObject *) pyml_unwrap(array_type_ocaml); PyObject *result = PyArray_New( PyArray_SubType, 1, &length, NPY_DOUBLE, NULL, data, 0, NPY_ARRAY_CARRAY, NULL); CAMLreturn(pyml_wrap(result, true)); } CAMLprim value pyarray_move_floatarray_wrapper(value numpy_array_ocaml, value array_ocaml) { CAMLparam2(numpy_array_ocaml, array_ocaml); pyml_assert_initialized(); PyObject *numpy_array = pyml_unwrap(numpy_array_ocaml); PyArrayObject_fields *fields = (PyArrayObject_fields *) pyobjectdescr(numpy_array); fields->data = (void *) array_ocaml; CAMLreturn(Val_unit); } CAMLprim value PyLong_FromString_wrapper(value str_ocaml, value base_ocaml) { CAMLparam2(str_ocaml, base_ocaml); CAMLlocal1(result); pyml_assert_initialized(); const char *str = String_val(str_ocaml); const char *pend; int base = Int_val(base_ocaml); PyObject *l = Python_PyLong_FromString(str, &pend, base); ssize_t len = pend - str; result = caml_alloc_tuple(2); Store_field(result, 0, pyml_wrap(l, true)); Store_field(result, 1, Val_int(len)); CAMLreturn(result); } CAMLprim value Python27_PyCapsule_IsValid_wrapper(value arg0_ocaml, value arg1_ocaml) { CAMLparam2(arg0_ocaml, arg1_ocaml); pyml_assert_initialized(); if (!Python27_PyCapsule_IsValid) { caml_failwith("PyCapsule_IsValid is only available in Python >2.7"); } PyObject *arg0 = pyml_unwrap(arg0_ocaml); const char *arg1 = String_val(arg1_ocaml); int result = Python27_PyCapsule_IsValid(arg0, arg1); CAMLreturn(Val_int(result)); } CAMLprim value pyml_pyframe_new(value filename_ocaml, value funcname_ocaml, value lineno_ocaml) { CAMLparam3(filename_ocaml, funcname_ocaml, lineno_ocaml); const char *filename = String_val(filename_ocaml); const char *funcname = String_val(funcname_ocaml); int lineno = Int_val(lineno_ocaml); PyObject *code = Python_PyCode_NewEmpty(filename, funcname, lineno); PyObject *globals = Python_PyDict_New(); PyObject *result = Python_PyFrame_New( Python_PyThreadState_Get(), code, globals, NULL); Py_DECREF(code); Py_DECREF(globals); CAMLreturn(pyml_wrap(result, true)); } #include "pyml_wrappers.inc" pyml_stubs.h000066400000000000000000000155031452051003100134230ustar00rootroot00000000000000#ifndef _PYML_STUBS_H_ #define _PYML_STUBS_H_ #include #include #include #include /* The following definitions are extracted and simplified from #include */ /* ssize_t is POSIX and not defined with Visual Studio */ /* See for instance https://github.com/vlm/asn1c/issues/159 */ #if defined(_MSC_VER) #include typedef SSIZE_T ssize_t; #else #include #endif typedef ssize_t Py_ssize_t; #define _PyObject_HEAD_EXTRA #define PyObject_HEAD \ _PyObject_HEAD_EXTRA \ Py_ssize_t ob_refcnt; \ PyObject *ob_type; typedef void PyObject; typedef struct { Py_ssize_t ob_refcnt; PyObject *ob_type; } PyObjectDescr; PyObjectDescr *pyobjectdescr(PyObject *obj); typedef struct { PyObjectDescr ob_base; Py_ssize_t ob_size; } PyVarObject; typedef void (*destructor)(PyObject *); typedef struct _typeobject { PyVarObject ob_base; const char *tp_name; Py_ssize_t tp_basicsize, tp_itemsize; destructor tp_dealloc; void *tp_print; void *tp_getattr; void *tp_setattr; void *tp_as_async; void *tp_repr; void *tp_as_number; void *tp_as_sequence; void *tp_as_mapping; void *tp_hash; void *tp_call; void *tp_str; void *tp_getattro; void *tp_setattro; void *tp_as_buffer; unsigned long tp_flags; const char *tp_doc; void *tp_traverse; void *tp_clear; void *tp_richcompare; Py_ssize_t tp_weaklistoffset; void *tp_iter; void *tp_iternext; void *tp_methods; void *tp_members; void *tp_getset; void *tp_base; PyObject *tp_dict; void *tp_descr_get; void *tp_descr_set; Py_ssize_t tp_dictoffset; void *tp_init; void *tp_alloc; void *tp_new; void *tp_free; void *tp_is_gc; PyObject *tp_bases; PyObject *tp_mro; PyObject *tp_cache; PyObject *tp_subclasses; PyObject *tp_weaklist; void *tp_del; unsigned int tp_version_tag; void *tp_finalize; /* #ifdef COUNT_ALLOCS */ Py_ssize_t tp_allocs; Py_ssize_t tp_frees; Py_ssize_t tp_maxalloc; struct _typeobject *tp_prev; struct _typeobject *tp_next; /* #endif */ } PyTypeObject; void pyml_assert_initialized(); void pyml_assert_python2(); void pyml_assert_ucs2(); void pyml_assert_ucs4(); void pyml_assert_python3(); /* Numpy */ /* from ndarraytypes.h */ enum NPY_TYPES { NPY_BOOL=0, NPY_BYTE, NPY_UBYTE, NPY_SHORT, NPY_USHORT, NPY_INT, NPY_UINT, NPY_LONG, NPY_ULONG, NPY_LONGLONG, NPY_ULONGLONG, NPY_FLOAT, NPY_DOUBLE, NPY_LONGDOUBLE, NPY_CFLOAT, NPY_CDOUBLE, NPY_CLONGDOUBLE, NPY_OBJECT=17, NPY_STRING, NPY_UNICODE, NPY_VOID, /* * New 1.6 types appended, may be integrated * into the above in 2.0. */ NPY_DATETIME, NPY_TIMEDELTA, NPY_HALF, NPY_NTYPES, NPY_NOTYPE, NPY_CHAR, /* special flag */ NPY_USERDEF=256, /* leave room for characters */ /* The number of types not including the new 1.6 types */ NPY_NTYPES_ABI_COMPATIBLE=21 }; #define NPY_ARRAY_C_CONTIGUOUS 0x0001 #define NPY_ARRAY_F_CONTIGUOUS 0x0002 #define NPY_ARRAY_OWNDATA 0x0004 #define NPY_ARRAY_ALIGNED 0x0100 #define NPY_ARRAY_WRITEABLE 0x0400 #define NPY_ARRAY_BEHAVED (NPY_ARRAY_ALIGNED | \ NPY_ARRAY_WRITEABLE) #define NPY_ARRAY_CARRAY (NPY_ARRAY_C_CONTIGUOUS | \ NPY_ARRAY_BEHAVED) #define NPY_ARRAY_FARRAY (NPY_ARRAY_F_CONTIGUOUS | \ NPY_ARRAY_BEHAVED) /* From pyport.h */ typedef intptr_t Py_intptr_t; /* From npy_common.h */ typedef Py_intptr_t npy_intp; void ** pyml_get_pyarray_api(PyObject *c_api); #define Py_INCREF(op) \ ((pyobjectdescr(op))->ob_refcnt++) #define Py_XINCREF(op) \ do { \ PyObjectDescr *_py_xincref_tmp = \ pyobjectdescr((PyObject *)(op)); \ if (_py_xincref_tmp != NULL) \ Py_INCREF(_py_xincref_tmp); \ } while (0) #define Py_DECREF(op) \ do { \ PyObjectDescr *_py_decref_tmp = \ pyobjectdescr((PyObject *)(op)); \ if (--(_py_decref_tmp)->ob_refcnt == 0) \ ((struct _typeobject *) \ pyobjectdescr(_py_decref_tmp->ob_type)) \ ->tp_dealloc(op); \ } while (0) /* from ndarraytypes.h */ typedef struct _PyArray_Descr { PyObject_HEAD PyTypeObject *typeobj; char kind; char type; char byteorder; char flags; int type_num; int elsize; int alignment; struct _arr_descr *subarray; PyObject *fields; PyObject *names; void *f; PyObject *metadata; void *c_metadata; int hash; } PyArray_Descr; typedef struct _arr_descr { PyArray_Descr *base; PyObject *shape; } PyArray_ArrayDescr; typedef struct tagPyArrayObject_fields { PyObject_HEAD char *data; int nd; npy_intp *dimensions; npy_intp *strides; PyObject *base; PyArray_Descr *descr; int flags; PyObject *weakreflist; } PyArrayObject_fields; typedef struct { int cf_flags; int cf_feature_version; /* Python >=3.8 */ } 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_ */ pyml_tests.ml000066400000000000000000000577531452051003100136230ustar00rootroot00000000000000let () = Pyml_tests_common.add_test ~title:"version" (fun () -> Printf.printf "Python version %s\n%!" (Py.version ()); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"library version" (fun () -> Printf.printf "Python library version %s\n%!" (Py.get_version ()); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"hello world" (fun () -> assert (Py.Run.simple_string "print('Hello world!')"); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"class" (fun () -> let m = Py.Import.add_module "test" in let value_obtained = ref None in let callback arg = value_obtained := Some (Py.String.to_string (Py.Tuple.get_item arg 1)); Py.none in let c = Py.Class.init "myClass" ~methods:[("callback", Py.Callable.of_function_as_tuple callback)] in Py.Module.set m "myClass" c; assert (Py.Run.simple_string " from test import myClass myClass().callback('OK') "); assert (!value_obtained = Some "OK"); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"empty tuple" (fun () -> assert (Py.Tuple.create 0 = Py.Tuple.empty); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"make tuple" (fun () -> assert (Py.Tuple.to_singleton (Py.Tuple.singleton (Py.Long.of_int 0)) = Py.Long.of_int 0); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"module get/set/remove" (fun () -> let m = Py.Module.create "test" in Py.Module.set m "test" Py.none; assert (Py.Module.get m "test" = Py.none); Py.Module.remove m "test"; begin try ignore (Py.Module.get m "test"); Pyml_tests_common.Failed "Should have been removed" with Py.E _ -> Pyml_tests_common.Passed end) let () = Pyml_tests_common.add_test ~title:"capsule" (fun () -> let (wrap, unwrap) = Py.Capsule.make "string" in let m = Py.Import.add_module "test" in let pywrap args = let s = Py.String.to_string args.(0) in wrap s in let pyunwrap args = let s = unwrap args.(0) in Py.String.of_string s in Py.Module.set_function m "wrap" pywrap; Py.Module.set_function m "unwrap" pyunwrap; assert (Py.Run.simple_string " from test import wrap, unwrap x = wrap('OK') print('Capsule type: {0}'.format(x)) assert unwrap(x) == 'OK' "); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"capsule-conversion-error" (fun () -> let ref_str = "foobar" in let ref_pair1 = (3.141592, 42) in let ref_pair2 = (2.71828182846, 42) in let (wrap_str, unwrap_str) = Py.Capsule.make "string-1" in let (wrap_pair, unwrap_pair) = Py.Capsule.make "pair-1" in let s = wrap_str ref_str in let p1 = wrap_pair ref_pair1 in let p2 = wrap_pair ref_pair2 in assert (unwrap_str s = ref_str); assert (unwrap_pair p1 = ref_pair1); assert (unwrap_pair p2 = ref_pair2); let unwrap_failed = try ignore (unwrap_pair s : float * int); false with _ -> true in assert unwrap_failed; let unwrap_failed = try ignore (unwrap_pair (Py.Long.of_int 42) : float * int); false with _ -> true in assert unwrap_failed; Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"exception" (fun () -> try let _ = Py.Run.eval ~start:Py.File " raise Exception('Great') " in Pyml_tests_common.Failed "uncaught exception" with Py.E (_, value) -> assert (Py.Object.to_string value = "Great"); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"ocaml exception" (fun () -> let m = Py.Import.add_module "test" in let mywrap _ = raise (Py.Err (Py.Err.Exception, "Great")) in Py.Module.set_function m "mywrap" mywrap; assert (Py.Run.simple_string " from test import mywrap try: mywrap() raise Exception('No exception raised') except Exception as err: assert str(err) == \"Great\" "); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"ocaml exception with traceback" (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} ] in let mywrap _ = raise (Py.Err_with_traceback (Py.Err.Exception, "Great", traceback)) in Py.Module.set_function m "mywrap" mywrap; assert (Py.Run.simple_string " from test import mywrap import sys import traceback try: mywrap() raise Exception('No exception raised') except Exception as err: if sys.version_info.major == 3 and sys.version_info.minor >= 7: 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 == ['', 'file2.ml', 'file1.ml'] assert str(err) == \"Great\" "); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"restore with null" (fun () -> try let _ = Py.Run.eval ~start:Py.File " raise Exception('Great') " in Pyml_tests_common.Failed "uncaught exception" with Py.E (_, value) -> begin assert (Py.Object.to_string value = "Great"); match Py.Err.fetched () with | None -> Pyml_tests_common.Failed "unexpected none" | Some (err, _args, _traceback) -> (* Test that using [Py.Err.restore] on null works fine. *) Py.Err.restore err Py.null Py.null; Py.Err.clear (); Pyml_tests_common.Passed end) let () = Pyml_tests_common.add_test ~title:"ocaml other exception" (fun () -> let m = Py.Import.add_module "test" in let mywrap _ = raise Exit in Py.Module.set_function m "mywrap" mywrap; try ignore (Py.Run.eval ~start:File " from test import mywrap try: mywrap() except Exception as err: raise Exception('Should not be caught by Python') "); Pyml_tests_common.Failed "Uncaught exception" with Exit -> Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"run file with filename" (fun () -> let result = Pyutils.with_temp_file "print(\"Hello, world!\")" begin fun file _channel -> Py.Run.load (Py.Filename file) "test.py" end in if result = Py.none then Pyml_tests_common.Passed else let result_str = Py.Object.to_string result in let msg = Printf.sprintf "Result None expected but got %s" result_str in Pyml_tests_common.Failed msg ) let () = Pyml_tests_common.add_test ~title:"run file with channel" (Pyml_tests_common.enable_only_on_unix (fun () -> let result = Pyutils.with_temp_file "print(\"Hello, world!\")" begin fun _file channel -> Py.Run.load (Py.Channel channel) "test.py" end in if result = Py.none then Pyml_tests_common.Passed else let result_str = Py.Object.to_string result in let msg = Printf.sprintf "Result None expected but got %s" result_str in Pyml_tests_common.Failed msg ) ) let () = Pyml_tests_common.add_test ~title:"boolean" (fun () -> try if not (Py.Bool.to_bool (Py.Run.eval "True")) then Pyml_tests_common.Failed "true is false" else if Py.Bool.to_bool (Py.Run.eval "False") then Pyml_tests_common.Failed "false is true" else 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" (fun () -> Gc.full_major (); Py.finalize (); begin try assert (Py.Run.simple_string "not initialized"); raise Exit with Failure _ -> () | Exit -> failwith "Uncaught not initialized" end; let (version, minor) = !Pyml_tests_common.use_version in Py.initialize ~verbose:true ?version ?minor (); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"string conversion error" (fun () -> try let _ = Py.String.to_string (Py.Long.of_int 0) in Pyml_tests_common.Failed "uncaught exception" with Py.E (_, value) -> Printf.printf "Caught exception: %s\n%!" (Py.Object.to_string value); Pyml_tests_common.Passed | Failure s -> Printf.printf "Caught failure: %s\n%!" s; Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"float conversion error" (fun () -> try let _ = Py.Float.to_float (Py.String.of_string "a") in Pyml_tests_common.Failed "uncaught exception" with Py.E (_, value) -> Printf.printf "Caught exception: %s\n%!" (Py.Object.to_string value); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"long conversion error" (fun () -> try let _ = Py.Long.to_int (Py.String.of_string "a") in Pyml_tests_common.Failed "uncaught exception" with Py.E (_, value) -> Printf.printf "Caught exception: %s\n%!" (Py.Object.to_string value); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"iterators" (fun () -> let iter = Py.Object.get_iter (Py.Run.eval "['a','b','c']") in let list = Py.Iter.to_list_map Py.String.to_string iter in assert (list = ["a"; "b"; "c"]); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Iterator.create" (fun () -> let m = Py.Import.add_module "test" in let iter = Py.Iter.of_list_map Py.Int.of_int [3; 1; 4; 1; 5] in Py.Module.set m "ocaml_iterator" iter; assert (Py.Run.simple_string " from test import ocaml_iterator res = 0 for v in ocaml_iterator: res += v "); 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 assert (list = ["a"; "b"; "c"]); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Iterator.create_call" (fun () -> let iter_of_list python_of list = let list = ref list in let next () = match !list with | [] -> None | p :: q -> list := q; Some (python_of p) in Py.Iter.create_call next in let m = Py.Import.add_module "test" in let iter = iter_of_list Py.Int.of_int [3; 1; 4; 1; 5] in Py.Module.set m "ocaml_iterator2" iter; assert (Py.Run.simple_string " from test import ocaml_iterator2 res = 0 for v in ocaml_iterator2: res += v "); 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 assert (list = ["a"; "b"; "c"]); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Dict.iter" (fun () -> let dict = Py.Dict.create () in for i = 0 to 9 do Py.Dict.set_item_string dict (string_of_int i) (Py.Long.of_int i) done; let table = Array.make 10 None in Py.Dict.iter begin fun key value -> let index = Py.Long.to_int value in assert (table.(index) = None); table.(index) <- Some (Py.String.to_string key) end dict; Array.iteri begin fun i v -> match v with None -> failwith "None!" | Some v' -> assert (i = int_of_string v') end table; Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"unicode" (fun () -> let codepoints = [| 8203; 127; 83; 2384; 0; 12 |] in let python_string = Py.String.of_unicode codepoints in let ocaml_string = Py.String.to_string python_string in let python_string' = Py.String.decode_UTF8 ocaml_string in let codepoints' = Py.String.to_unicode python_string' in assert (codepoints = codepoints'); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"interactive loop" (Pyml_tests_common.enable_only_on_unix (fun () -> Pyutils.with_stdin_from_string "42" Py.Run.interactive (); assert (Py.Long.to_int (Py.last_value ()) = 42); Pyml_tests_common.Passed)) let () = Pyml_tests_common.add_test ~title:"IPython" (Pyml_tests_common.enable_only_on_unix (Py.Run.frame (Pyutils.with_stdin_from_string "exit" (fun () -> if Py.Import.try_import_module "IPython" = None then Pyml_tests_common.Disabled "IPython is not available" else begin Py.Run.ipython ~frame:false (); Pyml_tests_common.Passed end)))) let () = Pyml_tests_common.add_test ~title:"Marshal" (fun () -> let v = Py.Long.of_int 42 in let m = Py.Marshal.dumps v in let v' = Py.Marshal.loads m in assert (Py.Long.to_int v' = 42); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Py.List.of_list" (fun () -> let v = Py.List.of_list [Py.Long.of_int 42] in assert (Py.List.length v = 1); assert (Py.Long.to_int (Py.List.get v 0) = 42); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Py.List.sort" (fun () -> let pi_digits = [ 3; 1; 4; 1; 5; 9; 2; 6; 5; 3; 5; 8 ] in let v = Py.List.of_list [] in assert (Py.List.length v = 0); let count = Py.Object.call_method v "count" [|Py.Long.of_int 1|] in assert (Py.Long.to_int count = 0); List.iter (fun i -> ignore (Py.Object.call_method v "append" [|Py.Long.of_int i|])) pi_digits; let count = Py.Object.call_method v "count" [|Py.Long.of_int 1|] in assert (Py.Long.to_int count = 2); assert (Py.List.length v = List.length pi_digits); let _ = Py.Object.call_method v "sort" [||] in let sorted_digits = List.map Py.Int.to_int (Py.List.to_list v) in assert (sorted_digits = List.sort compare pi_digits); (* No `clear' method in lists in Python 2 *) if Py.version_major () >= 3 then begin let _ = Py.Object.call_method v "clear" [||] in assert (Py.List.length v = 0) end; Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"array" (fun () -> let array = [| 1; 2 |] in let a = Py.Array.of_array Py.Long.of_int Py.Long.to_int array in let m = Py.Import.add_module "test" in Py.Module.set m "array" a; assert (Py.Run.simple_string " from test import array assert len(array) == 2 assert array[0] == 1 assert array[1] == 2 array[0] = 42 array[1] = 43 copy = [] for x in array: copy.append(x) assert copy == [42, 43] "); assert (array.(0) = 42); assert (array.(1) = 43); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"numpy" (fun () -> if Py.Import.try_import_module "numpy" = None then Pyml_tests_common.Disabled "numpy is not available" else begin let array = Stdcompat.Array.Floatarray.create 2 in Stdcompat.Array.Floatarray.set array 0 1.; Stdcompat.Array.Floatarray.set array 1 2.; let a = Py.Array.numpy array in let m = Py.Import.add_module "test" in Py.Module.set m "array" a; assert (Py.Run.simple_string " from test import array assert len(array) == 2 assert array[0] == 1. assert array[1] == 2. array[0] = 42. array[1] = 43. "); assert (Stdcompat.Array.Floatarray.get array 0 = 42.); assert (Stdcompat.Array.Floatarray.get array 1 = 43.); Pyml_tests_common.Passed end) let () = Pyml_tests_common.add_test ~title:"numpy crunch" (fun () -> if Py.Import.try_import_module "numpy" = None then Pyml_tests_common.Disabled "numpy is not available" else begin let array = Stdcompat.Float.Array.init 0x10000 float_of_int in let numpy_array = Py.Array.numpy array in let add = Py.Module.get_function (Py.Import.import_module "numpy") "add" in let rec crunch numpy_array n = if n <= 0 then numpy_array else let array = Stdcompat.Float.Array.map_from_array Stdcompat.Fun.id (Py.Sequence.to_array_map Py.Float.to_float (add [| numpy_array; numpy_array |])) in crunch (Py.Array.numpy array) (pred n) in ignore (crunch (Py.Array.numpy array) 0x100); assert (Stdcompat.Float.Array.length array = 0x10000); for i = 0 to 0x10000 - 1 do assert (Stdcompat.Float.Array.get array i = float_of_int i) done; Stdcompat.Float.Array.set array 1 42.; assert (Py.Float.to_float (Py.Sequence.get numpy_array 1) = 42.); Pyml_tests_common.Passed end) let () = Pyml_tests_common.add_test ~title:"none" (fun () -> let none = Py.none in assert (none = Py.none); assert (Py.is_none none); assert (Py.Type.get none = None); let none = Py.Run.eval "None" in assert (none = Py.none); assert (Py.is_none none); assert (Py.Type.get none = None); let not_none = Py.Long.of_int 42 in assert (not_none <> Py.none); assert (not (Py.is_none not_none)); assert (Py.Type.get not_none <> None); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"docstring" (fun () -> Gc.full_major (); let fn = let docstring = Printf.sprintf "test%d" 42 in Py.Callable.of_function ~docstring (fun _ -> Py.none) in Gc.full_major (); let other_string = Printf.sprintf "test%d" 43 in let doc = Py.Object.get_attr_string fn "__doc__" in begin match doc with None -> failwith "None!" | Some doc -> assert (Py.String.to_string doc = "test42") end; ignore other_string; Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"function-name" (fun () -> let run make_name expect_name = Gc.full_major (); let fn = let name = make_name () in Py.Callable.of_function ?name (fun _ -> Py.none) in Gc.full_major (); let other_string = Printf.sprintf "test%d" 43 in let name = Py.Object.get_attr_string fn "__name__" in begin match name with None -> failwith "None!" | Some doc -> assert (Py.String.to_string doc = expect_name) end; ignore other_string in run (fun () -> Some (Printf.sprintf "test%d" 42)) "test42"; run (fun () -> None) "anonymous_closure"; Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"is-instance" (fun () -> let forty_two = Py.Int.of_int 42 in let forty_two_str = Py.String.of_string "42" in let int = Py.Dict.find_string (Py.Eval.get_builtins ()) "int" in assert (Py.Object.is_instance forty_two int); assert (not (Py.Object.is_instance forty_two_str int)); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"is-subclass" (fun () -> let int = Py.Dict.find_string (Py.Eval.get_builtins ()) "int" in let cls1 = Py.Class.init ~parents:[int] "cls1" in let cls2 = Py.Class.init ~parents:[cls1] "cls2" in assert (Py.Object.is_subclass cls1 int); assert (not (Py.Object.is_subclass int cls1)); assert (Py.Object.is_subclass cls2 cls1); assert (not (Py.Object.is_subclass cls1 cls2)); assert (Py.Object.is_subclass cls2 int); assert (not (Py.Object.is_subclass int cls2)); Pyml_tests_common.Passed ) let () = Pyml_tests_common.add_test ~title:"Set" (fun () -> let set = Py.Set.create () in for i = 0 to 9 do Py.Set.add set (Py.Long.of_int i) done; assert (Py.Set.check set); assert (Py.Set.size set = 10); Py.Set.discard set (Py.Long.of_int 5); assert (Py.Set.size set = 9); let values = Py.Set.to_list_map Py.Long.to_int set in assert (values = [0; 1; 2; 3; 4; 6; 7; 8; 9]); let set' = Py.Set.copy set in Py.Set.add set' (Py.Long.of_int 42); Py.Set.add set' (Py.Long.of_int 42); assert (Py.Set.size set = 9); assert (Py.Set.size set' = 10); 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 () = Pyml_tests_common.add_test ~title:"get_attr_string" (fun () -> let bool_ty = Py.Object.get_type Py.Bool.t in assert (Py.Object.get_attr_string bool_ty "dtype" = None); Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Dict.find fails with Not_found" (fun () -> let dict = Py.Dict.create() in try let _ = Py.Dict.find dict Py.Tuple.empty in Pyml_tests_common.Failed "Unexpected found" with Not_found -> Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Dict.find_string fails with Not_found" (fun () -> let dict = Py.Dict.create() in try let _ = Py.Dict.find_string dict "key" in Pyml_tests_common.Failed "Unexpected found" with Not_found -> Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Object.find_attr fails with Not_found if key is missing" (fun () -> try let _ = Py.Object.find_attr Py.Tuple.empty (Py.String.of_string "key") in Pyml_tests_common.Failed "Unexpected found" with Not_found -> Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Object.find_attr fails with Python exception if wrong key type" (fun () -> try let _ = Py.Object.find_attr Py.Tuple.empty Py.Tuple.empty in Pyml_tests_common.Failed "Unexpected found" with Py.E _ -> Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Object.find_attr_string fails with Not_found if key is missing" (fun () -> try let _ = Py.Object.find_attr_string Py.Tuple.empty "key" in Pyml_tests_common.Failed "Unexpected found" with Not_found -> Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Object.find fails with Not_found if key is missing" (fun () -> try let _ = Py.Object.find (Py.Dict.create ()) (Py.String.of_string "key") in Pyml_tests_common.Failed "Unexpected found" with Not_found -> Pyml_tests_common.Passed) let () = Pyml_tests_common.add_test ~title:"Object.find fails with Not_found if wrong key type" (fun () -> try let _ = Py.Object.find Py.Tuple.empty Py.Tuple.empty in Pyml_tests_common.Failed "Unexpected found" with Py.E _ -> Pyml_tests_common.Passed) let () = if not !Sys.interactive then Pyml_tests_common.main () pyml_tests_common.ml000066400000000000000000000052741452051003100151620ustar00rootroot00000000000000let use_version = ref (None, None) type status = | Passed | Failed of string | Disabled of string let tests = Queue.create () let add_test ~title f = Queue.add (title, f) tests let failed = ref false let launch_test (title, f) = Printf.printf "Test '%s' ... %!" title; try match f () with Passed -> Printf.printf "passed\n%!" | Failed reason -> Printf.printf "failed: %s\n%!" reason; failed := true | Disabled reason -> Printf.printf "disabled: %s\n%!" reason with Py.E (ty, value) -> Printf.printf "raised a Python exception: [%s] %s\n%!" (Py.Object.to_string ty) (Py.Object.to_string value); failed := true | e -> Printf.printf "raised an exception: %s\n%!" (Printexc.to_string e); failed := true let rec launch_tests () = match try Some (Queue.pop tests) with Queue.Empty -> None with None -> () | Some test -> launch_test test; launch_tests () let enable_only_on_unix f arg = if Sys.os_type = "Unix" then f arg else Disabled "only on Unix" let show_environment_variable envvar = try Printf.eprintf "%s=%s\n" envvar (Sys.getenv envvar) with Not_found -> Printf.eprintf "%s not set\n" envvar let main () = let library_name, version, minor = match Sys.argv with [| _ |] -> None, None, None | [| _; version |] -> begin match String.length version with 1 -> None, Some (int_of_string version), None | (3 | 4) when version.[1] = '.' -> None, Some (int_of_string (String.sub version 0 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 use_version := (version, minor); prerr_endline "Environment variables:"; show_environment_variable "PATH"; show_environment_variable "PYTHONHOME"; 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 ~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 prerr_endline "Not a debug build."; prerr_endline "Starting tests..."; launch_tests (); if !failed then exit 1 pyml_tests_common.mli000066400000000000000000000004371452051003100153270ustar00rootroot00000000000000type status = | Passed | Failed of string | Disabled of string val add_test: title:string -> (unit -> status) -> unit val use_version: (int option * int option) ref val enable_only_on_unix: ('a -> status) -> 'a -> status val launch_tests : unit -> unit val main: unit -> unit pyops.ml.405000066400000000000000000000000001452051003100130460ustar00rootroot00000000000000pyops.ml.new000066400000000000000000000010311452051003100133340ustar00rootroot00000000000000let ( .@() ) = Py.Object.find_attr let ( .@$() ) = Py.Object.find_attr_string let ( .@()<- ) = Py.Object.set_attr let ( .@$()<- ) = Py.Object.set_attr_string let ( .![] ) = Py.Object.find let ( .!$[] ) = Py.Object.find_string let ( .![]<- ) = Py.Object.set_item let ( .!$[]<- ) = Py.Object.set_item_string let ( .%[] ) = Py.Dict.find let ( .%$[] ) = Py.Dict.find_string let ( .%[]<- ) = Py.Dict.set_item let ( .%$[]<- ) = Py.Dict.set_item_string let ( .&() ) = Py.Module.get_function let ( .&()<- ) = Py.Module.set_function pyops.mli.405000066400000000000000000000000001452051003100132170ustar00rootroot00000000000000pyops.mli.new000066400000000000000000000030011452051003100135040ustar00rootroot00000000000000val ( .@() ) : Py.Object.t -> Py.Object.t -> Py.Object.t (** Equivalent to {!Py.Object.find_attr}. *) val ( .@$() ) : Py.Object.t -> string -> Py.Object.t (** Equivalent to {!Py.Object.find_attr_string}. *) val ( .@()<- ) : Py.Object.t -> Py.Object.t -> Py.Object.t -> unit (** Equivalent to {!Py.Object.set_attr}. *) val ( .@$()<- ) : Py.Object.t -> string -> Py.Object.t -> unit (** Equivalent to {!Py.Object.set_attr_string}. *) val ( .![] ) : Py.Object.t -> Py.Object.t -> Py.Object.t (** Equivalent to {!Py.Object.find}. *) val ( .!$[] ) : Py.Object.t -> string -> Py.Object.t (** Equivalent to {!Py.Object.find_string}. *) val ( .![]<- ) : Py.Object.t -> Py.Object.t -> Py.Object.t -> unit (** Equivalent to {!Py.Object.set_item}. *) val ( .!$[]<- ) : Py.Object.t -> string -> Py.Object.t -> unit (** Equivalent to {!Py.Object.set_item_string}. *) val ( .%[] ) : Py.Object.t -> Py.Object.t -> Py.Object.t (** Equivalent to {!Py.Dict.find}. *) val ( .%$[] ) : Py.Object.t -> string -> Py.Object.t (** Equivalent to {!Py.Dict.find_string}. *) val ( .%[]<- ) : Py.Object.t -> Py.Object.t -> Py.Object.t -> unit (** Equivalent to {!Py.Dict.set_item}. *) val ( .%$[]<- ) : Py.Object.t -> string -> Py.Object.t -> unit (** Equivalent to {!Py.Dict.set_item_string}. *) val ( .&() ) : Py.Object.t -> string -> Py.Object.t array -> Py.Object.t (** Equivalent to {!Py.Module.get_function}. *) val ( .&()<- ) : Py.Object.t -> string -> (Py.Object.t array -> Py.Object.t) -> unit (** Equivalent to {!Py.Module.set_function}. *) pytop.ml000066400000000000000000000005161452051003100125540ustar00rootroot00000000000000let eval_exn str = let lexbuf = Lexing.from_string str in let phrase = !Toploop.parse_toplevel_phrase lexbuf in Toploop.execute_phrase false Format.err_formatter phrase let () = assert (eval_exn (Printf.sprintf "#directory \"%s\";;" Pymltop_libdir.libdir)); assert (eval_exn "#install_printer Py.Object.format_repr;;") pytypes.ml000066400000000000000000000012321452051003100131120ustar00rootroot00000000000000type pyobject type compare = LT | LE | EQ | NE | GT | GE type input = Single | File | Eval let int_of_compare c = match c with LT -> 0 | LE -> 1 | EQ -> 2 | NE -> 3 | GT -> 4 | GE -> 5 let compare_of_int c = match c with 0 -> LT | 1 -> LE | 2 -> EQ | 3 -> NE | 4 -> GT | 5 -> GE | _ -> failwith "Pytypes.compare_of_int" let input_of_int input = match input with 256 -> Single | 257 -> File | 258 -> Eval | _ -> failwith "Pytypes.input_of_int" type 'a file = Filename of string | Channel of 'a let file_map f x = match x with Filename filename -> Filename filename | Channel channel -> Channel (f channel) pytypes.mli000066400000000000000000000004501452051003100132640ustar00rootroot00000000000000type pyobject type compare = LT | LE | EQ | NE | GT | GE type input = Single | File | Eval val int_of_compare: compare -> int val compare_of_int: int -> compare val input_of_int: int -> input type 'a file = Filename of string | Channel of 'a val file_map: ('a -> 'b) -> 'a file -> 'b file pyutils.ml000066400000000000000000000045311452051003100131130ustar00rootroot00000000000000open Stdcompat let option_find f x = try Some (f x) with Not_found -> None let substring_between string before after = String.sub string before (after - before) let int_of_octal octal = int_of_string ("0o" ^ octal) let int_of_hex hex = int_of_string ("0x" ^ hex) let split_left_on_char ?(from=0) char s = try substring_between s from (String.index_from s from char) with Not_found -> if from = 0 then s else substring_between s from (String.length s) let split_right_on_char ?(from=0) char s = try substring_between s (String.index_from s from char + 1) (String.length s) with Not_found -> if from = 0 then s else substring_between s from (String.length s) let trim_carriage_return line = let length = String.length line in if String.sub line (length - 1) 1 = "\r" then String.sub line 0 (length - 1) else line let input_lines channel = let accu = ref [] in try while true do accu := trim_carriage_return (input_line channel) :: !accu; done; assert false with End_of_file -> List.rev !accu let write_and_close channel f arg = try let result = f arg in close_out channel; result with e -> close_out_noerr channel; raise e 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; Stdcompat.In_channel.with_open_bin file (f file) end ~finally:(fun () -> Sys.remove file) let with_pipe f = let (read, write) = Unix.pipe () in let in_channel = Unix.in_channel_of_descr read and out_channel = Unix.out_channel_of_descr write in Fun.protect begin fun () -> f in_channel out_channel end ~finally:begin fun () -> close_in_noerr in_channel; close_out_noerr out_channel end let with_stdin_from channel f arg = let stdin_backup = Unix.dup Unix.stdin in Unix.dup2 (Unix.descr_of_in_channel channel) Unix.stdin; Fun.protect begin fun () -> f arg end ~finally:begin fun () -> Unix.dup2 stdin_backup Unix.stdin end let with_channel_from_string s f = with_pipe begin fun in_channel out_channel -> output_string out_channel s; close_out out_channel; f in_channel end let with_stdin_from_string s f arg = with_channel_from_string s (fun channel -> with_stdin_from channel f arg) pyutils.mli000066400000000000000000000054441452051003100132700ustar00rootroot00000000000000(** This module declares utility functions that does not require Python to be initialized. *) val substring_between: string -> int -> int -> string (** [substring_between s i j] returns the substring of [s] between the indices [i] (included) and [j] (excluded). *) val int_of_octal: string -> int (** Returns the integer represented by the argument written in base 8. *) val int_of_hex: string -> int (** Returns the integer represented by the argument written in base 16. *) val split_left_on_char: ?from:int -> char -> string -> string (** If the character occurs in the substring beginning from [from], returns the prefix that precedes the first occurrence (excluded), else returns the whole substring beginning from [from]. *) val split_right_on_char: ?from:int -> char -> string -> string (** If the character occurs in the substring beginning from [from], returns the suffix that succedes the first occurrence (excluded), else returns the whole substring beginning from [from]. *) val trim_carriage_return: string -> string (** If the string ends with ['\r'], then returns the string without this character, else returns the whole string. *) 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. *) val option_find: ('a -> 'b) -> 'a -> 'b option (** [option_find f x] returns [Some (f x)], or [None] if [f x] raises [Not_found]. *) 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]. [channel] is always closed after [f] has been called, even if [f] raises an exception. *) val with_temp_file: string -> (string -> in_channel -> 'a) -> 'a (** [with_temp_file s f] creates a temporary file with [s] as contents and calls [f filename in_channel] where [filename] is the name of the temporary file and [in_channel] is an input channel opened to read the file. The file is deleted after the execution of [f] (even if [f] raised an exception. *) val with_pipe: (in_channel -> out_channel -> 'a) -> 'a (** [with_pipe f] creates a pipe and calls [f] with the two ends of the pipe. *) val with_stdin_from: in_channel -> ('a -> 'b) -> 'a -> 'b (** [with_stdin_from chan f arg] calls [f arg] with the standard input redirected for reading from [chan]. *) val with_channel_from_string: string -> (in_channel -> 'a) -> 'a (** [with_channel_from_string s f] calls [f in_channel] where [in_channel] is an input channel returning the contents of [s]. *) val with_stdin_from_string: string -> ('a -> 'b) -> 'a -> 'b (** [with_stdin_from_string s f arg] calls [f arg] with the standard input redirected for reading from the contents of [s]. *) pyutop.ml000066400000000000000000000000331452051003100127330ustar00rootroot00000000000000let () = UTop_main.main ()