Skip to main content

A library for providing inter-language foreign function interface calls

Project description

A library for providing inter-language foreign function interface calls

Abstract

METACALL Python Port is the port of METACALL to Python Programming Language. With METACALL Python Port you can transparently execute code from Python to any programming language, for example, calling JavaScript, NodeJS, Ruby or C# code from Python.

Install

Install MetaCall binaries first:

curl -sL https://raw.githubusercontent.com/metacall/install/master/install.sh | bash

Then install MetaCall Python package through MetaCall:

pip3 install metacall

Example

Calling Ruby from Python

multiply.rb

def multiply(left, right)
    return left * right
end

main.py

from metacall import metacall_load_from_file, metacall

metacall_load_from_file('rb', [ 'multiply.rb' ])

metacall('multiply', 3, 4); # 12

Calling NodeJS from Python (MonkeyPatch API)

sum.js

module.exports = {
    sum: (x, y) => x + y,
};

main.py

import metacall
from sum.js import sum

sum(3, 4); # 7

Running the example:

python3 main.py

Using pointers (calling to a C library)

For a simple case, let’s imagine that we have a simple C function that has an ‘in’ parameter and we want to pass a pointer to a long, from Python side, and then store some value there for reading it later on. Let’s assume we have a loadtest.h and libloadtest.so and a C function from this library could be this one:

void modify_int_ptr(long *l)
{
    *l = 111;
}

Now if we want to call it from Python side, we should do the following:

from metacall import metacall_load_from_package, metacall, metacall_value_reference, metacall_value_dereference

# Load the library (we can configure the search paths for the .so and .lib with metacall_execution_path)
# metacall_execution_path('c', '/usr/local/include')
# metacall_execution_path('c', '/usr/local/lib')
metacall_load_from_package('c', 'loadtest')

# Create value pointer (int *)
int_val = 324444
int_val_ref = metacall_value_reference(int_val)

# Pass the pointer to the function
metacall('modify_int_ptr', int_val_ref)

# Get the value from pointer
int_val_deref = metacall_value_dereference(int_val_ref)
print(int_val_deref, '==', 111)

For a more complex case, where we have an in/out parameter, for example an opaque struct that we want to alloc from C side. First of all, with the following header loadtest.h:

#ifndef LIB_LOAD_TEST_H
#define LIB_LOAD_TEST_H 1

#if defined(WIN32) || defined(_WIN32)
    #define EXPORT __declspec(dllexport)
#else
    #define EXPORT __attribute__((visibility("default")))
#endif

#ifdef __cplusplus
extern "C" {
#endif

#include <cstdint>

typedef struct
{
    uint32_t i;
    double d;
} pair;

typedef struct
{
    uint32_t size;
    pair *pairs;
} pair_list;

EXPORT int pair_list_init(pair_list **t);

EXPORT double pair_list_value(pair_list *t, uint32_t id);

EXPORT void pair_list_destroy(pair_list *t);

#ifdef __cplusplus
}
#endif

#endif /* LIB_LOAD_TEST_H */

With the following implementation loadtest.cpp:

#include "loadtest.h"

int pair_list_init(pair_list **t)
{
    static const uint32_t size = 3;

    *t = new pair_list();

    (*t)->size = size;
    (*t)->pairs = new pair[(*t)->size];

    for (uint32_t i = 0; i < size; ++i)
    {
        (*t)->pairs[i].i = i;
        (*t)->pairs[i].d = (double)(((double)i) * 1.0);
    }

    return 0;
}

double pair_list_value(pair_list *t, uint32_t id)
{
    return t->pairs[id].d;
}

void pair_list_destroy(pair_list *t)
{
    delete[] t->pairs;
    delete t;
}

In this case the structs are not opaque, but they can be opaque and it will work in the same way. Now, we can call those functions in the following manner:

from metacall import metacall_load_from_package, metacall, metacall_value_create_ptr, metacall_value_reference, metacall_value_dereference

metacall_load_from_package('c', 'loadtest')

# Create a pointer to void* set to NULL
list_pair = metacall_value_create_ptr(None)

# Create a reference to it (void**)
list_pair_ref = metacall_value_reference(list_pair)

# Call the function
result = metacall('pair_list_init', list_pair_ref)

# Get the result updated (struct allocated)
list_pair = metacall_value_dereference(list_pair_ref)

# Pass it to a function
result = metacall('pair_list_value', list_pair, 2)

# Destroy it
metacall('pair_list_destroy', list_pair)

# Here result will be 2.0 because is the third element in the array of pairs inside the struct
print(result, '==', 2.0)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

metacall-0.5.3.tar.gz (10.6 kB view details)

Uploaded Source

Built Distribution

metacall-0.5.3-py3-none-any.whl (7.8 kB view details)

Uploaded Python 3

File details

Details for the file metacall-0.5.3.tar.gz.

File metadata

  • Download URL: metacall-0.5.3.tar.gz
  • Upload date:
  • Size: 10.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.3

File hashes

Hashes for metacall-0.5.3.tar.gz
Algorithm Hash digest
SHA256 f3b99d643f9b464ef61781f548a92909b720402cd9cf81fe50e30837f6213a78
MD5 ee7116c714a3867ab9b8c61c72073c71
BLAKE2b-256 1a0951182df17ea85d5ba257913aa9e675b6087c6d942a62f91b5697e1ab5fb1

See more details on using hashes here.

File details

Details for the file metacall-0.5.3-py3-none-any.whl.

File metadata

  • Download URL: metacall-0.5.3-py3-none-any.whl
  • Upload date:
  • Size: 7.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.3

File hashes

Hashes for metacall-0.5.3-py3-none-any.whl
Algorithm Hash digest
SHA256 2d44abbdaa76f30f9e9c801ce20b60d6efbfe83261f70f655a2c06986c7ba840
MD5 f6fe1d04716727931033a74f78f5a86a
BLAKE2b-256 ea0af1c71ba2ac476358a71b52772227735c3d619c503441c93d708d72b6987d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page