Skip to content

Commit

Permalink
Add the class PyObjectPtr to manage reference counting.
Browse files Browse the repository at this point in the history
  • Loading branch information
bcoconni committed Nov 10, 2024
1 parent b57db80 commit ebc6f69
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 93 deletions.
125 changes: 37 additions & 88 deletions python/PyLogger.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,25 @@ PyLogger::PyLogger(PyObject* logger)
{
if (PyObject_IsInstance(logger, FGLogger_PyClass)) {
logger_pyclass = logger;
Py_INCREF(logger_pyclass);
Py_INCREF(logger);

convert_level_enums[LogLevel::BULK] = PyObject_GetAttrString(LogLevel_PyClass, "BULK");
convert_level_enums[LogLevel::DEBUG] = PyObject_GetAttrString(LogLevel_PyClass, "DEBUG");
convert_level_enums[LogLevel::INFO] = PyObject_GetAttrString(LogLevel_PyClass, "INFO");
convert_level_enums[LogLevel::WARN] = PyObject_GetAttrString(LogLevel_PyClass, "WARN");
convert_level_enums[LogLevel::ERROR] = PyObject_GetAttrString(LogLevel_PyClass, "ERROR");
convert_level_enums[LogLevel::FATAL] = PyObject_GetAttrString(LogLevel_PyClass, "FATAL");

convert_format_enums[LogFormat::RESET] = PyObject_GetAttrString(LogFormat_PyClass, "RESET");
convert_format_enums[LogFormat::RED] = PyObject_GetAttrString(LogFormat_PyClass, "RED");
convert_format_enums[LogFormat::BLUE] = PyObject_GetAttrString(LogFormat_PyClass, "BLUE");
convert_format_enums[LogFormat::CYAN] = PyObject_GetAttrString(LogFormat_PyClass, "CYAN");
convert_format_enums[LogFormat::GREEN] = PyObject_GetAttrString(LogFormat_PyClass, "GREEN");
convert_format_enums[LogFormat::DEFAULT] = PyObject_GetAttrString(LogFormat_PyClass, "DEFAULT");
convert_format_enums[LogFormat::BOLD] = PyObject_GetAttrString(LogFormat_PyClass, "BOLD");
convert_format_enums[LogFormat::NORMAL] = PyObject_GetAttrString(LogFormat_PyClass, "NORMAL");
convert_format_enums[LogFormat::UNDERLINE_ON] = PyObject_GetAttrString(LogFormat_PyClass, "UNDERLINE_ON");
convert_format_enums[LogFormat::UNDERLINE_OFF] = PyObject_GetAttrString(LogFormat_PyClass, "UNDERLINE_OFF");
} else {
PyErr_SetString(PyExc_TypeError, "The logger must be an instance of FGLogger");
}
Expand All @@ -50,127 +68,58 @@ PyLogger::PyLogger(PyObject* logger)
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void PyLogger::SetLevel(LogLevel level) {
PyObject* py_level;

switch (level)
{
case LogLevel::BULK:
py_level = PyObject_GetAttrString(LogLevel_PyClass, "BULK");
break;
case LogLevel::DEBUG:
py_level = PyObject_GetAttrString(LogLevel_PyClass, "DEBUG");
break;
case LogLevel::INFO:
py_level = PyObject_GetAttrString(LogLevel_PyClass, "INFO");
break;
case LogLevel::WARN:
py_level = PyObject_GetAttrString(LogLevel_PyClass, "WARN");
break;
case LogLevel::ERROR:
py_level = PyObject_GetAttrString(LogLevel_PyClass, "ERROR");
break;
case LogLevel::FATAL:
py_level = PyObject_GetAttrString(LogLevel_PyClass, "FATAL");
break;
}

bool success = CallPythonMethodWithArguments("set_level", py_level);
if (success) FGLogger::SetLevel(level);
Py_DECREF(py_level);
assert(convert_level_enums.find(level) != convert_level_enums.end());
PyObjectPtr py_level = convert_level_enums[level];
PyObjectPtr result = CallPythonMethodWithArguments("set_level", py_level);
if (result) FGLogger::SetLevel(level);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void PyLogger::FileLocation(const std::string& filename, int line)
{
PyObject* py_filename = PyUnicode_FromString(filename.c_str());
PyObject* py_line = PyLong_FromLong(line);
PyObject* args = PyTuple_Pack(2, py_filename, py_line);
PyObjectPtr py_filename = PyUnicode_FromString(filename.c_str());
PyObjectPtr py_line = PyLong_FromLong(line);
PyObjectPtr args = PyTuple_Pack(2, py_filename.get(), py_line.get());
CallPythonMethodWithTuple("file_location", args);
Py_DECREF(args);
Py_DECREF(py_filename);
Py_DECREF(py_line);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void PyLogger::Message(const std::string& message)
{
PyObject* msg = PyUnicode_FromString(message.c_str());
PyObjectPtr msg = PyUnicode_FromString(message.c_str());
CallPythonMethodWithArguments("message", msg);
Py_DECREF(msg);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

void PyLogger::Format(LogFormat format)
{
PyObject* py_format;

switch (format)
{
case LogFormat::RESET:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "RESET");
break;
case LogFormat::RED:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "RED");
break;
case LogFormat::BLUE:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "BLUE");
break;
case LogFormat::CYAN:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "CYAN");
break;
case LogFormat::GREEN:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "GREEN");
break;
case LogFormat::DEFAULT:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "DEFAULT");
break;
case LogFormat::BOLD:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "BOLD");
break;
case LogFormat::NORMAL:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "NORMAL");
break;
case LogFormat::UNDERLINE_ON:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "UNDERLINE_ON");
break;
case LogFormat::UNDERLINE_OFF:
py_format = PyObject_GetAttrString(LogFormat_PyClass, "UNDERLINE_OFF");
break;
}

assert(convert_format_enums.find(format) != convert_format_enums.end());
PyObjectPtr py_format = convert_format_enums[format];
CallPythonMethodWithArguments("format", py_format);
Py_DECREF(py_format);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool PyLogger::CallPythonMethodWithArguments(const char* method_name, PyObject* arg)
PyObjectPtr PyLogger::CallPythonMethodWithArguments(const char* method_name, const PyObjectPtr& arg)
{
PyObject* tuple = PyTuple_Pack(1, arg);
bool success = CallPythonMethodWithTuple(method_name, tuple);
Py_DECREF(tuple);
return success;
PyObjectPtr tuple = PyTuple_Pack(1, arg.get());
return CallPythonMethodWithTuple(method_name, tuple);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool PyLogger::CallPythonMethodWithTuple(const char* method_name, PyObject* tuple)
PyObjectPtr PyLogger::CallPythonMethodWithTuple(const char* method_name, const PyObjectPtr& tuple)
{
PyObject* method = PyObject_GetAttrString(logger_pyclass, method_name);
PyObjectPtr method = PyObject_GetAttrString(logger_pyclass.get(), method_name);
assert(method); // This should not fail as the constructor has checked the type of logger_pyclass.

PyObject* result = PyObject_CallObject(method, tuple);
Py_DECREF(method);
PyObjectPtr result = PyObject_CallObject(method.get(), tuple.get());

if (result) {
Py_DECREF(result);
return true;
}
if (!result) PyErr_Print();

PyErr_Print();
return false;
return result;
}
} // namespace JSBSim
65 changes: 61 additions & 4 deletions python/PyLogger.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
found on the world wide web at http://www.gnu.org.
*/

#include <map>
#include <Python.h>
#include "input_output/FGLog.h"

Expand All @@ -36,22 +37,78 @@ extern PyObject* FGLogger_PyClass;
extern PyObject* LogLevel_PyClass;
extern PyObject* LogFormat_PyClass;

// Helper class to manage the reference count of a PyObject.
class PyObjectPtr {
public:
PyObjectPtr(PyObject* obj = nullptr) noexcept
: object(obj) {}

// Copy constructor
PyObjectPtr(const PyObjectPtr& other) noexcept {
object = other.object;
Py_XINCREF(object);
}

// Move constructor
PyObjectPtr(PyObjectPtr&& other) noexcept
: object(other.object)
{
other.object = nullptr;
}

// Copy assignment operator
PyObjectPtr& operator=(const PyObjectPtr& other) noexcept {
if (this != &other) {
Py_XDECREF(object); // Decrement the reference count of the current object
object = other.object;
Py_XINCREF(object); // Increment the reference count of the new object
}
return *this;
}

PyObjectPtr& operator=(PyObject* src) noexcept {
Py_XDECREF(object);
object = src;
return *this;
}

// Move assignment operator
PyObjectPtr& operator=(PyObjectPtr&& other) noexcept {
if (this != &other) {
Py_XDECREF(object); // Decrement the reference count of the current object
object = other.object;
other.object = nullptr; // Prevent the source from decrementing the reference count
}
return *this;
}

~PyObjectPtr() noexcept { Py_XDECREF(object); }

PyObject* get() const noexcept { return object; }
operator bool() const noexcept { return object != nullptr; }

protected:
PyObject* object;
};


class PyLogger : public FGLogger
{
public:
explicit PyLogger(PyObject* logger);
~PyLogger() override { Py_XDECREF(logger_pyclass); }
void SetLevel(LogLevel level) override;
void FileLocation(const std::string& filename, int line) override;
void Message(const std::string& message) override;
void Format(LogFormat format) override;
void Flush(void) override { CallPythonMethodWithTuple("flush", nullptr); }

private:
bool CallPythonMethodWithTuple(const char* method_name, PyObject* tuple);
bool CallPythonMethodWithArguments(const char* method_name, PyObject* arg);
PyObjectPtr CallPythonMethodWithTuple(const char* method_name, const PyObjectPtr& tuple);
PyObjectPtr CallPythonMethodWithArguments(const char* method_name, const PyObjectPtr& arg);

PyObject* logger_pyclass = nullptr;
PyObjectPtr logger_pyclass;
std::map<LogLevel, PyObjectPtr> convert_level_enums;
std::map<LogFormat, PyObjectPtr> convert_format_enums;
};
}
#endif
2 changes: 1 addition & 1 deletion python/jsbsim.pyx.in
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def inherit_docstring(cls):
class DefaultLogger(FGLogger):
"""Default logger: print messages to stdout without formatting."""
def file_location(self, filename: str, line: int) -> None:
print(f"\nIn file {filename}: line {line}", end='')
print(f"\nIn file {filename}: line {line}")
def message(self, message: str) -> None:
print(f"{message}", end='')

Expand Down

0 comments on commit ebc6f69

Please sign in to comment.