OCaml-Python Bindings Generator

code on GitHub

Generate Python bindings with pyml directly from OCaml value specifications.

While you could write all your Python bindings by hand, it can be tedious and it gets old real quick. While pyml_bindgen can't yet auto-generate all the bindings you may need, it can definitely take care of a lot of the tedious and repetitive work you need to do when writing bindings for a big Python library!! 💖

How to get started

Getting started with a new package or library and going through lots of docs can be frustrating. Here's the order I would suggest you look at these docs

  • Read the installing and quick start sections of this page.
  • Then read through the getting started tutorial. If you only read one page in the docs, make it this one! It explains most of what you need to know to get started with a simple example, while not getting bogged down in too much details.
  • Next, you can either peruse the rules for writing value specifications that pyml_bindgen can understand, or check out more examples.

If you have any questions or issues, please let me know about it on GitHub!

Installing

pyml_bindgen is available on Opam. You can install it in the normal way:

$ opam install pyml_bindgen

You can also install from source. pyml_bindgen is a Dune project, so you should be able to clone the repository and build it with dune as long as you have the proper dependencies installed 🤞

$ git clone https://github.com/mooreryan/pyml_bindgen.git
$ cd pyml_bindgen
$ opam install . --deps-only --with-doc --with-test
$ dune test && dune build --profile=release && dune install
$ pyml_bindgen --help
... help screen should show up ...

Quick start

pyml_bindgen is a CLI program that generates OCaml modules that bind Python classes via pyml.

Here's a small example. Take a Python class, Thing. (Put it in a file called thing.py...this means the Python module will be called thing.)

class Thing:
    def __init__(self, x):
        self.x = x

    def add(self, y):
        return self.x + y

Now, look at your Python class and decide how you would like to use this class on the OCaml side.

For now, we will just do a direct translation, keeping in mind the rules for writing value specs that pyml_bindgen can process. Maybe something like this. (Put it in a file called val_specs.txt.)

val __init__ : x:int -> unit -> t

val x : t -> int

val add : t -> y:int -> unit -> int

Finally, to generate the OCaml code, run the pyml_bindgen program. There are a couple of options you can choose, but let's just keep it simple for now.

$ pyml_bindgen val_specs.txt thing Thing --caml-module=Thing > lib.ml
$ ocamlformat --enable-outside-detected-project lib.ml

And here's the output of the ocamlformat command.

module Thing : sig
  type t

  val of_pyobject : Pytypes.pyobject -> t option

  val to_pyobject : t -> Pytypes.pyobject

  val __init__ : x:int -> unit -> t

  val x : t -> int

  val add : t -> y:int -> unit -> int
end = struct
  let filter_opt l = List.filter_map Fun.id l

  let import_module () = Py.Import.import_module "thing"

  type t = Pytypes.pyobject

  let is_instance pyo =
    let py_class = Py.Module.get (import_module ()) "Thing" in
    Py.Object.is_instance pyo py_class

  let of_pyobject pyo = if is_instance pyo then Some pyo else None

  let to_pyobject x = x

  let __init__ ~x () =
    let callable = Py.Module.get (import_module ()) "Thing" in
    let kwargs = filter_opt [ Some ("x", Py.Int.of_int x) ] in
    of_pyobject @@ Py.Callable.to_function_with_keywords callable [||] kwargs

  let x t = Py.Int.to_int @@ Py.Object.find_attr_string t "x"

  let add t ~y () =
    let callable = Py.Object.find_attr_string t "add" in
    let kwargs = filter_opt [ Some ("y", Py.Int.of_int y) ] in
    Py.Int.to_int @@ Py.Callable.to_function_with_keywords callable [||] kwargs
end

Check out the examples for more info about using and running pyml_bindgen. Then, check out the rules that you have to follow when writing value specifications that pyml_bindgen can read.

License

Software

license MIT or Apache
2.0

Copyright (c) 2021 Ryan M. Moore.

Licensed under the Apache License, Version 2.0 or the MIT license, at your option. This program may not be copied, modified, or distributed except according to those terms.

Documentation

Creative Commons License

Copyright (c) 2021 Ryan M. Moore.

This documentation is licensed under a Creative Commons Attribution 4.0 International License.