Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python 3.11, 3.12, and 3.13 support #501

Merged
merged 13 commits into from
Dec 10, 2024
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
Loading