You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+7-7Lines changed: 7 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,17 +4,17 @@
4
4
5
5
Generate Python bindings with [pyml](https://github.com/thierry-martinez/pyml) directly from OCaml value specifications.
6
6
7
-
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!! 💖
7
+
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!! 💖
8
8
9
9
## Quick start
10
10
11
-
First, install `pyml_bindgen`. It is available on [Opam](https://opam.ocaml.org/packages/pyml_bindgen/).
11
+
First, install `pyml_bindgen`. It is available on [Opam](https://opam.ocaml.org/packages/pyml_bindgen/).
12
12
13
13
```
14
14
$ opam install pyml_bindgen
15
15
```
16
16
17
-
Say you have a Python class you want to bind and use in OCaml. (Filename: `adder.py`)
17
+
Say you have a Python class you want to bind and use in OCaml. (Filename: `adder.py`)
18
18
19
19
```python
20
20
classAdder:
@@ -23,7 +23,7 @@ class Adder:
23
23
return x + y
24
24
```
25
25
26
-
To do so, you write OCaml value specifications for the class and methods you want to bind. (Filename: `val_specs.txt`)
26
+
To do so, you write OCaml value specifications for the class and methods you want to bind. (Filename: `val_specs.txt`)
Now you can use your generated functions in your OCaml code. (Filename: `run.ml`)
38
+
Now you can use your generated functions in your OCaml code. (Filename: `run.ml`)
39
39
40
40
```ocaml
41
41
open Lib
@@ -63,13 +63,13 @@ $ dune exec ./run.exe
63
63
64
64
For information on installing and using `pyml_bindgen`, check out the [docs](https://mooreryan.github.io/ocaml_python_bindgen/).
65
65
66
-
Additionally, you can find examples in the [examples](https://github.com/mooreryan/ocaml_python_bindgen/tree/main/examples) directory. One neat thing about these examples is that you can see how to write Dune [rules](https://dune.readthedocs.io/en/stable/dune-files.html#rule) to automatically generate your `pyml` bindings.
66
+
Additionally, you can find examples in the [examples](https://github.com/mooreryan/ocaml_python_bindgen/tree/main/examples) directory. One neat thing about these examples is that you can see how to write Dune [rules](https://dune.readthedocs.io/en/stable/dune-files.html#rule) to automatically generate your `pyml` bindings.
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.
*Note: You can find in-depth examples of binding dictionaries [here](dictionaries.md) and [here](dictionaries-2.md). A lot of the same ideas apply to tuples, and they are currently better explained in those links!*
3
+
As of version 0.3.0, you can handle certain types of tuples directly.
4
4
5
-
Tuples are sort of weird....As of now, `pyml_bindgen` can't handle tuples directly :(
5
+
- You can now bind tuples with 2, 3, 4, or 5 elements.
6
+
- They can be passed in as arguments, or returned from functions.
7
+
- Only basic types and Python objects are allowed in tuples.
8
+
- You can also put tuples inside of collections, e.g., `(int * string) list`, but not Options or Or_errors.
6
9
7
-
For now what you need to do is to create a little helper module that "wraps" the tuple you need to pass in to Python or return from Python. *(Or just write the binding by hand...)*
10
+
If you need something more complicated then that, you will have to use some of the same tricks I talk about in the [dictionaries](dictionaries.md) or [dictionaries-2](dictionaries-2.md) help pages.
8
11
9
-
Say you need to get an `int * string` tuple in and out of Python. You should make a module something like this:
12
+
## Examples
10
13
11
-
```ocaml
12
-
module rec Tuple_int_string : sig
13
-
type t
14
-
15
-
val make : int -> string -> t
16
-
17
-
val to_pyobject : t -> Pytypes.pyobject
18
-
val of_pyobject : Pytypes.pyobject -> t
19
-
20
-
val print_endline : t -> unit
21
-
end = struct
22
-
type t = int * string
23
-
24
-
let make i s = (i, s)
25
-
26
-
let to_pyobject (i, s) =
27
-
Py.Tuple.of_tuple2 (Py.Int.of_int i, Py.String.of_string s)
28
-
29
-
let of_pyobject pyo =
30
-
let i, s = Py.Tuple.to_tuple2 pyo in
31
-
(Py.Int.to_int i, Py.String.to_string s)
32
-
33
-
let print_endline (i, s) = print_endline @@ string_of_int i ^ " " ^ s
34
-
end
35
-
```
36
-
37
-
Then you can put that with the code that `pyml_bindgen` generates for whatever class you're actually trying to bind. Perhaps something like this...
38
-
39
-
```ocaml
40
-
module rec Tuple_int_string : sig
41
-
...
42
-
end = struct
43
-
...
44
-
end
45
-
46
-
and My_cool_thing : sig
47
-
48
-
...
49
-
50
-
val foo : x:Tuple_int_string.t -> unit -> Tuple_int_string.t
51
-
52
-
...
53
-
54
-
end
55
-
```
56
-
57
-
(You get the idea.)
58
-
59
-
In the val specs that you write, just refer to the `Tuple_int_string` module like any other:
60
-
61
-
```ocaml
62
-
val foo : x:Tuple_int_string.t -> unit -> Tuple_int_string.t
63
-
```
64
-
65
-
The key here is to have a module that has working `to_pyobject` and `of_pyobject` functions. If these two functions know how to properly get your type/module into and out of Python-land, then it should work :)
66
-
67
-
There is a Cram test [here](https://github.com/mooreryan/pyml_bindgen/tree/main/test/binding_tuples.t) that illustrates this idea. Just note that some of the bash stuff in the `run.t` file is to automate it, but you'd probably do that part by hand.
68
-
69
-
*Note: At some point, I will work up a full example with tuples and other types that you can't yet deal with directly in `pyml_bindgen`.
14
+
You can find examples of binding tuples [here](https://github.com/mooreryan/ocaml_python_bindgen/tree/main/examples/tuples).
-`Pytypes.pyobject` or `Py.Object.t` if you need to deal with `pytypes` directly
21
+
- Certain kinds of tuples
22
+
23
+
Note that your custom types must be newly minted modules. E.g.,
23
24
24
25
```ocaml
25
26
(* This is okay *)
@@ -38,35 +39,39 @@ let doc_to_pyobject ...
38
39
39
40
## Return types
40
41
41
-
For return types, you can use all of the above types plus `unit`, and `'a Or_error.t` for types `'a` other than `unit`. However, you cannot use `unit array`, `unit list`, or `unit Seq.t`. This is because I haven't decided the best way to handle `unit` and `None` (that's Python's `None`) quite yet!
42
+
For return types, you can use all of the above types plus `unit`, and `'a Or_error.t` for types `'a` other than `unit`. However, you cannot use `unit array`, `unit list`, or `unit Seq.t`. This is because I haven't decided the best way to handle `unit` and `None` (that's Python's `None`) quite yet!
43
+
44
+
You can also return many kinds of tuples directly. See [here](tuples.md).
42
45
43
46
## Nesting
44
47
45
-
Note: currently, you're not allowed to have **nested**`array`, `list`, `Seq.t`, or `Or_error.t`. If you need them, you will have to bind those functions by hand :)
48
+
Note: currently, you're not allowed to have **nested**`array`, `list`, `Seq.t`, or `Or_error.t`. If you need them, you will have to bind those functions by hand :)
46
49
47
50
E.g., `'a array list` will fail.
48
51
49
52
You are allowed to nest `'a option` in arrays, lists, and `Seq.t`s (e.g., `'a option list`); however, this will not work with `Or_error.t`.
50
53
51
54
## Pytypes
52
55
53
-
Sometimes you may want to deal directly with `Pytypes.pyobject` (a.k.a. `Py.Object.t`). Maybe you have a Python function that is truly polymorphic, or you just don't feel like giving a function a specific OCaml type for whatever reason. Regardless, you can use `Pytypes.pyobject` or `Py.Object.t` for this. Of course, you will be leaking a bit of the `pyml` implementation into your API, but sometimes that is unavoidable, or just more convenient than dealing with it in another way.
56
+
Sometimes you may want to deal directly with `Pytypes.pyobject` (a.k.a. `Py.Object.t`). Maybe you have a Python function that is truly polymorphic, or you just don't feel like giving a function a specific OCaml type for whatever reason. Regardless, you can use `Pytypes.pyobject` or `Py.Object.t` for this. Of course, you will be leaking a bit of the `pyml` implementation into your API, but sometimes that is unavoidable, or just more convenient than dealing with it in another way.
54
57
55
58
Note that you currently are not allowed to nest `pytypes` in any of the containers or monads.
56
59
57
-
## Dictionaries & Tuples
60
+
## Tuples
58
61
59
-
See [here](dictionaries.md) and[here](dictionaries-2.md) for examples of binding dictionaries.
62
+
You can handle many kinds of tuples directly. See[here](tuples.md).
60
63
61
-
If you need to pass or return tuples to Python functions, see [here](tuples.md); however, the same ideas apply to tuples as are covered in the above links for dictionaries.
64
+
## Dictionaries
65
+
66
+
See [here](dictionaries.md) and [here](dictionaries-2.md) for examples of binding dictionaries.
62
67
63
68
Alternatively, you could mark them as `Pytypes.pyobject` or `Py.Object.t` and let the caller deal with them in some way.
64
69
65
70
## Placeholders
66
71
67
72
There are two placeholders you can use: `todo` and `not_implemented`.
68
73
69
-
If you're binding a large library and you aren't planning on implementing a function, but you want it in the signature for whatever reason, you can use `not_implemented`. If you are planning to come back and implement a function later, you can use `todo`.
74
+
If you're binding a large library and you aren't planning on implementing a function, but you want it in the signature for whatever reason, you can use `not_implemented`. If you are planning to come back and implement a function later, you can use `todo`.
0 commit comments