Skip to content

Commit

Permalink
Merge pull request #501 from lf-lang/python-12
Browse files Browse the repository at this point in the history
Python 3.11, 3.12, and 3.13 support
  • Loading branch information
edwardalee authored Dec 10, 2024
2 parents 862e0b9 + a252c62 commit 3e63031
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 28 deletions.
7 changes: 5 additions & 2 deletions .github/workflows/clang-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ on: [pull_request]

jobs:
clang-format:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- name: Install clang-tidy
- name: Install clang-tidy and clang-format
run: |
sudo apt-get update
sudo apt-get install -y clang-tidy
sudo apt-get install -y pipx
pipx install clang-format
clang-format --version
- name: Analyze
run: make format-check
6 changes: 3 additions & 3 deletions core/federated/federate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1951,9 +1951,9 @@ void lf_connect_to_rti(const char* hostname, int port) {
if (result < 0)
continue; // Connect failed.

// Have connected to an RTI, but not sure it's the right RTI.
// Send a MSG_TYPE_FED_IDS message and wait for a reply.
// Notify the RTI of the ID of this federate and its federation.
// Have connected to an RTI, but not sure it's the right RTI.
// Send a MSG_TYPE_FED_IDS message and wait for a reply.
// Notify the RTI of the ID of this federate and its federation.

#ifdef FEDERATED_AUTHENTICATED
LF_PRINT_LOG("Connected to an RTI. Performing HMAC-based authentication using federation ID.");
Expand Down
2 changes: 1 addition & 1 deletion include/core/utils/impl/hashmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#define V void*
#endif
#ifndef HASH_OF
#define HASH_OF(key) (size_t) key
#define HASH_OF(key) (size_t)key
#endif
#ifndef HASHMAP
#define HASHMAP(token) hashmap##_##token
Expand Down
2 changes: 1 addition & 1 deletion include/core/utils/impl/pointer_hashmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#define HASHMAP(token) hashmap_object2int##_##token
#define K void*
#define V int
#define HASH_OF(key) (size_t) key
#define HASH_OF(key) (size_t)key
#include "hashmap.h"
#undef HASHMAP
#undef K
Expand Down
63 changes: 61 additions & 2 deletions python/lib/modal_models/impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ static PyObject* py_mode_set(PyObject* mode_capsule, PyObject* args) {
lf_print_error("Null pointer received.");
exit(1);
}
Py_INCREF(m->mode);

self_base_t* self = PyCapsule_GetPointer(m->lf_self, "lf_self");
if (self == NULL) {
lf_print_error("Null pointer received.");
exit(1);
}
Py_INCREF(m->lf_self);

_LF_SET_MODE_WITH_TYPE(mode, m->change_type);

Expand All @@ -61,6 +63,24 @@ static PyObject* py_mode_set(PyObject* mode_capsule, PyObject* args) {

//////////// Python Struct /////////////

/**
* Called when an mode in Python is to be created. Note that this is not normally
* used because modes are not created in Python.
*
* To initialize the mode_capsule, this function first calls the tp_alloc
* method of type mode_capsule_struct_t and then assign default values of NULL, NULL, 0
* to the members of the generic_mode_capsule_struct.
*/
PyObject* py_mode_capsule_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
mode_capsule_struct_t* self = (mode_capsule_struct_t*)type->tp_alloc(type, 0);
if (self != NULL) {
self->mode = NULL;
self->lf_self = NULL;
self->change_type = 0;
}
return (PyObject*)self;
}

/*
* The function members of mode_capsule.
* The set function is used to set a new mode.
Expand All @@ -69,6 +89,37 @@ static PyMethodDef mode_capsule_methods[] = {
{"set", (PyCFunction)py_mode_set, METH_NOARGS, "Set a new mode."}, {NULL} /* Sentinel */
};

/**
* Initialize the mode capsule "self" with NULL pointers and default change_type.
*/
static int py_mode_capsule_init(mode_capsule_struct_t* self, PyObject* args, PyObject* kwds) {
self->mode = NULL;
self->lf_self = NULL;
self->change_type = 0;
return 0;
}

/**
* Called when an mode capsule in Python is deallocated (generally
* called by the Python grabage collector).
* @param self
*/
void py_mode_capsule_dealloc(mode_capsule_struct_t* self) {
Py_XDECREF(self->mode);
Py_XDECREF(self->lf_self);
Py_TYPE(self)->tp_free((PyObject*)self);
}

/*
* The members of a mode_capsule that are accessible from a Python program, used to define
* a native Python type.
*/
PyMemberDef py_mode_capsule_members[] = {
{"mode", T_OBJECT, offsetof(mode_capsule_struct_t, mode), 0, "The pointer to the C mode struct"},
{"lf_self", T_OBJECT, offsetof(mode_capsule_struct_t, lf_self), 0, "Pointer to LF self"},
{NULL} /* Sentinel */
};

/*
* The definition of mode_capsule type object, which is
* used to describe how mode_capsule behaves.
Expand All @@ -79,7 +130,10 @@ static PyTypeObject mode_capsule_t = {
.tp_basicsize = sizeof(mode_capsule_struct_t),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = PyType_GenericNew,
.tp_new = py_mode_capsule_new,
.tp_init = (initproc)py_mode_capsule_init,
.tp_dealloc = (destructor)py_mode_capsule_dealloc,
.tp_members = py_mode_capsule_members,
.tp_methods = mode_capsule_methods,
};

Expand All @@ -100,6 +154,7 @@ void initialize_mode_capsule_t(PyObject* current_module) {
if (PyModule_AddObject(current_module, "mode_capsule", (PyObject*)&mode_capsule_t) < 0) {
Py_DECREF(&mode_capsule_t);
Py_DECREF(current_module);
lf_print_error_and_exit("Failed to initialize mode_capsule.");
return;
}
}
Expand All @@ -109,16 +164,19 @@ void initialize_mode_capsule_t(PyObject* current_module) {
*/
PyObject* convert_C_mode_to_py(reactor_mode_t* mode, self_base_t* lf_self, lf_mode_change_type_t change_type) {
// Create the mode struct in Python
mode_capsule_struct_t* cap = (mode_capsule_struct_t*)PyObject_GC_New(mode_capsule_struct_t, &mode_capsule_t);
mode_capsule_struct_t* cap = (mode_capsule_struct_t*)PyObject_New(mode_capsule_struct_t, &mode_capsule_t);

if (cap == NULL) {
lf_print_error_and_exit("Failed to convert mode.");
}
Py_INCREF(cap);

// Create the capsule to hold the reactor_mode_t* mode
PyObject* capsule = PyCapsule_New(mode, "mode", NULL);
if (capsule == NULL) {
lf_print_error_and_exit("Failed to convert mode.");
}
Py_INCREF(capsule);
// Fill in the Python mode struct.
cap->mode = capsule;

Expand All @@ -127,6 +185,7 @@ PyObject* convert_C_mode_to_py(reactor_mode_t* mode, self_base_t* lf_self, lf_mo
if (self_capsule == NULL) {
lf_print_error_and_exit("Failed to convert self.");
}
Py_INCREF(self_capsule);
cap->lf_self = self_capsule;

cap->change_type = change_type;
Expand Down
18 changes: 9 additions & 9 deletions python/lib/python_port.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,13 @@ PyObject* py_port_set(PyObject* self, PyObject* args) {
}

if (val) {
LF_PRINT_DEBUG("Setting value %p with reference count %d.", val, (int)Py_REFCNT(val));
// Py_INCREF(val);
// python_count_decrement(port->value);

lf_token_t* token = lf_new_token((void*)port, val, 1);
lf_set_destructor(port, python_count_decrement);
lf_set_token(port, token);
Py_INCREF(val);
LF_PRINT_DEBUG("Setting value %p with reference count %d.", val, (int)Py_REFCNT(val));

// Also set the values for the port capsule.
p->value = val;
Expand All @@ -117,9 +116,9 @@ PyObject* py_port_set(PyObject* self, PyObject* args) {
* garbage collector).
* @param self An instance of generic_port_instance_struct*
*/
void py_port_capsule_dealloc(generic_port_capsule_struct* self) {
Py_XDECREF(self->port);
Py_XDECREF(self->value);
static void py_port_capsule_dealloc(generic_port_capsule_struct* self) {
Py_CLEAR(self->port);
Py_CLEAR(self->value);
Py_TYPE(self)->tp_free((PyObject*)self);
}

Expand Down Expand Up @@ -147,7 +146,8 @@ PyObject* py_port_capsule_new(PyTypeObject* type, PyObject* args, PyObject* kwds
generic_port_capsule_struct* self;
self = (generic_port_capsule_struct*)type->tp_alloc(type, 0);
if (self != NULL) {
self->port = NULL;
Py_INCREF(Py_None);
self->port = Py_None;
Py_INCREF(Py_None);
self->value = Py_None;
self->is_present = false;
Expand Down Expand Up @@ -325,22 +325,22 @@ PyMappingMethods py_port_as_mapping = {(lenfunc)py_port_length, (binaryfunc)py_p
*/
int py_port_capsule_init(generic_port_capsule_struct* self, PyObject* args, PyObject* kwds) {
static char* kwlist[] = {"port", "value", "is_present", "width", "current_index", NULL};
PyObject *value = NULL, *tmp, *port = NULL;
PyObject *value = NULL, *port = NULL;

if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOp", kwlist, &port, &value, &self->is_present, &self->width,
&self->current_index)) {
return -1;
}

if (value) {
tmp = self->value;
PyObject* tmp = self->value;
Py_INCREF(value);
self->value = value;
Py_XDECREF(tmp);
}

if (port) {
tmp = self->port;
PyObject* tmp = self->port;
Py_INCREF(port);
self->port = port;
Py_XDECREF(tmp);
Expand Down
3 changes: 2 additions & 1 deletion python/lib/python_tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,11 @@ PyTypeObject PyTagType = {
* @return PyObject* The tag in Python.
*/
py_tag_t* convert_C_tag_to_py(tag_t c_tag) {
py_tag_t* py_tag = PyObject_GC_New(py_tag_t, &PyTagType);
py_tag_t* py_tag = PyObject_New(py_tag_t, &PyTagType);
if (py_tag == NULL) {
lf_print_error_and_exit("Failed to convert tag from C to Python.");
}
Py_INCREF(py_tag);
py_tag->tag = c_tag;
return py_tag;
}
17 changes: 14 additions & 3 deletions python/lib/pythontarget.c
Original file line number Diff line number Diff line change
Expand Up @@ -445,16 +445,18 @@ void destroy_action_capsule(PyObject* capsule) { free(PyCapsule_GetPointer(capsu
*/
PyObject* convert_C_port_to_py(void* port, int width) {
// Create the port struct in Python
PyObject* cap = (PyObject*)PyObject_GC_New(generic_port_capsule_struct, &py_port_capsule_t);
PyObject* cap = (PyObject*)PyObject_New(generic_port_capsule_struct, &py_port_capsule_t);
if (cap == NULL) {
lf_print_error_and_exit("Failed to convert port.");
}
Py_INCREF(cap);

// Create the capsule to hold the void* port
PyObject* capsule = PyCapsule_New(port, "port", NULL);
if (capsule == NULL) {
lf_print_error_and_exit("Failed to convert port.");
}
Py_INCREF(capsule);

// Fill in the Python port struct
((generic_port_capsule_struct*)cap)->port = capsule;
Expand Down Expand Up @@ -512,16 +514,18 @@ PyObject* convert_C_action_to_py(void* action) {
trigger_t* trigger = ((lf_action_base_t*)action)->trigger;

// Create the action struct in Python
PyObject* cap = (PyObject*)PyObject_GC_New(generic_action_capsule_struct, &py_action_capsule_t);
PyObject* cap = (PyObject*)PyObject_New(generic_action_capsule_struct, &py_action_capsule_t);
if (cap == NULL) {
lf_print_error_and_exit("Failed to convert action.");
}
Py_INCREF(cap);

// Create the capsule to hold the void* action
PyObject* capsule = PyCapsule_New(action, "action", NULL);
if (capsule == NULL) {
lf_print_error_and_exit("Failed to convert action.");
}
Py_INCREF(capsule);

// Fill in the Python action struct
((generic_action_capsule_struct*)cap)->action = capsule;
Expand Down Expand Up @@ -603,7 +607,14 @@ PyObject* get_python_function(string module, string class, int instance_id, stri

mbstowcs(wcwd, cwd, PATH_MAX);

Py_SetPath(wcwd);
// Deprecated: Py_SetPath(wcwd);
// Replace with the following more verbose version:
PyConfig config;
PyConfig_InitPythonConfig(&config);
// Add paths to the configuration
PyWideStringList_Append(&config.module_search_paths, wcwd);
// Initialize Python with the custom configuration
Py_InitializeFromConfig(&config);

LF_PRINT_DEBUG("Loading module %s in %s.", module, cwd);

Expand Down
9 changes: 3 additions & 6 deletions tag/api/tag.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,12 @@
#define NEVER_TAG \
(tag_t) { .time = NEVER, .microstep = NEVER_MICROSTEP }
// Need a separate initializer expression to comply with some C compilers
#define NEVER_TAG_INITIALIZER \
{ NEVER, NEVER_MICROSTEP }
#define NEVER_TAG_INITIALIZER {NEVER, NEVER_MICROSTEP}
#define FOREVER_TAG \
(tag_t) { .time = FOREVER, .microstep = FOREVER_MICROSTEP }
// Need a separate initializer expression to comply with some C compilers
#define FOREVER_TAG_INITIALIZER \
{ FOREVER, FOREVER_MICROSTEP }
#define ZERO_TAG \
(tag_t) { .time = 0LL, .microstep = 0u }
#define FOREVER_TAG_INITIALIZER {FOREVER, FOREVER_MICROSTEP}
#define ZERO_TAG (tag_t){.time = 0LL, .microstep = 0u}

// Returns true if timeout has elapsed.
#define CHECK_TIMEOUT(start, duration) (lf_time_physical() > ((start) + (duration)))
Expand Down

0 comments on commit 3e63031

Please sign in to comment.