Update README.md

Signed-off-by: dankeyy <dankeyy@protonmail.com>
This commit is contained in:
dankeyy 2023-02-17 01:19:10 +02:00 committed by GitHub
parent a871ec5d4a
commit 8fec0ff7e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,14 +1,15 @@
# Python Interop
This is an example of calling Roc code from [Python](https://www.python.org/).
This is a demo for calling Roc code from [Python](https://www.python.org/).
## Installation
The following was tested on NixOS, with Python 3.10, clang 13.0.1, gcc 11.3.0 but this should work on with most recent python3 and clang versions on most modern Linux and MacOS.\
Of course you're welcome to test on your machine and tell me if you ran into any issues or limitations.
The following was tested on NixOS, with `Python 3.10`, `clang 13.0.1`, `gcc 11.3.0` but should work with most recent versions of those on most modern Linux and MacOS.\
Of course you're welcome to test on your machine and tell me (via [Zulip](https://roc.zulipchat.com/#narrow/pm-with/583319-dank)) if you ran into any issues or limitations.
For your convenience, I've created a shell script (linux specific, you'll see why in a second) to take care of some rough edges (nothing too bad, mostly stuff like renames).\
But running random shell scripts may not be your cup of tea so let's first do a step by step walkthrough on how it works, and also provide general instructions for users of all operating systems:
> Because of some rough edges, the linux installation may be a bit more involved (nothing too bad, mostly stuff like renames), so for your convenience I made a small shell script to help out.
Now in favor of users of all OSs, let's first do a step by step walkthrough on how the build process works, and later, a few key notes on the implementation.
## Building the Roc library
@ -40,15 +41,18 @@ export LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH
```
That wasn't so bad and we're already done with prep work, all that's left it to build our C extension.
## Building the C Extension
``` sh
# If you want, you can set the environment variable cc, to compile with clang instead of gcc
python -m venv .interop_env
source .interop_env/bin/activate # /activate.fish if you're on fish
source .interop_env/bin/activate # activate.fish if you like fish
python setup.py install
```
For cleanness sake, we make virtual environment here, but do note you don't have to if you don't to, you can also `setup.py build` and grab the output shared object from the build/ directory.\
Shared objects are simply importable in CPython (which is great!), so you would be good to go start an interpreter in the same directory as your new demo.so and get the same result as the following-
For cleanness sake, we make virtual environment here, but do note you don't have to if you don't want to.
You can also run `python setup.py build` and grab the output shared object from the `build/lib.*` directory.\
Shared objects are simply importable in CPython (which is great!), so you can just start up an interpreter in the same directory as your new `demo.so` and get the same result.
**Note -** after all is said and done, for prolonged use, you may want to move your shared library (lib hello) to somewhere permanent on your `LD_LIBRARY_PATH`, or add your desired directory to `LD_LIBRARY_PATH` in some way (e.g put it in your shell .rc).
## Try it out!
@ -60,3 +64,19 @@ Now we can see our work by entering an interactive shell and calling our functio
>>> demo.call_roc(21)
'The number was 21, OH YEAH!!! 🤘🤘'
```
## Notes on implementation
The structure of python-interop is very similar to a C-Extension, in fact, it is one.\
We have:
- [`PyModuleDef demoModule`](https://docs.python.org/3/c-api/module.html#c.PyModuleDef) struct which declares the `demo` python module,
- [`PyMethodDef DemoMethods`](https://docs.python.org/3/c-api/structures.html#c.PyMethodDef) struct which declares `demo`'s methods,
- [`PyMODINIT_FUNC PyInit_demo`](https://docs.python.org/3/extending/extending.html) which is `demo`s initialization function, and of course,
- [`PyObject * call_roc`] which is our demo function! Getting args and returning our string to the interpreter. The Roc-Python bridge lives here, all the above are merely CPython boilerplate to wrap up a C implementation into a valid Python module.
The first three are basically the backbone of any C-API extension.\
In addition, a couple more functions and notes you may want to pay attention to:
- [`void roc_panic`] - When creating such interpreter-dependent code, it is reasonable to make the implementation of this function fire up an interpreter Exception (e.g `RuntimeError` or whatever suits).
- When I first came across another implementation, I was a bit confused about `extern void roc__mainForHost_1_exposed_generic`, so let me clarify - this is an external function, implemented by the application, that goes (on the application side-) by the name `mainForHost`.
And one last thing -
- When writing such the C glue (here, `demo.c`), you need to pay attention to not only Python's reference counting, but also Roc's, so remember to wear seatbelts and decrement your ref counts.