Skip to content

Commit 70dd47b

Browse files
committed
Avoid temp files by using Python directly
1 parent 723a9c1 commit 70dd47b

File tree

1 file changed

+97
-31
lines changed

1 file changed

+97
-31
lines changed

examples/python_adapters_embed.cpp

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ class PythonAdapters
4040
otio::SerializableObject::Retainer<otio::Timeline> const&,
4141
std::string const&,
4242
otio::ErrorStatus*);
43-
44-
private:
45-
bool _convert(std::string const& in_file_name, std::string const& out_file_name);
4643
};
4744

4845
PythonAdapters::PythonAdapters()
@@ -55,53 +52,122 @@ PythonAdapters::~PythonAdapters()
5552
Py_Finalize();
5653
}
5754

55+
class PyObjectRef
56+
{
57+
public:
58+
PyObjectRef(PyObject* o) :
59+
o(o)
60+
{
61+
if (!o)
62+
{
63+
throw std::runtime_error("Python error");
64+
}
65+
}
66+
67+
~PyObjectRef()
68+
{
69+
Py_XDECREF(o);
70+
}
71+
72+
PyObject* o = nullptr;
73+
74+
operator PyObject* () const { return o; }
75+
};
76+
5877
otio::SerializableObject::Retainer<otio::Timeline> PythonAdapters::read_from_file(
5978
std::string const& file_name,
6079
otio::ErrorStatus* error_status)
6180
{
62-
// Convert the input file to a temporary JSON file.
63-
const std::string temp_file_name = create_temp_dir() + "/temp.otio";
64-
if (!_convert(file_name, temp_file_name))
81+
otio::SerializableObject::Retainer<otio::Timeline> timeline;
82+
try
83+
{
84+
// Import the OTIO Python module.
85+
auto p_module = PyObjectRef(PyImport_ImportModule("opentimelineio.adapters"));
86+
87+
// Read the timeline into Python.
88+
auto p_read_from_file = PyObjectRef(PyObject_GetAttrString(p_module, "read_from_file"));
89+
auto p_read_from_file_args = PyObjectRef(PyTuple_New(1));
90+
const std::string file_name_normalized = normalize_path(file_name);
91+
auto p_read_from_file_arg = PyUnicode_FromStringAndSize(file_name_normalized.c_str(), file_name_normalized.size());
92+
if (!p_read_from_file_arg)
93+
{
94+
throw std::runtime_error("cannot create arg");
95+
}
96+
PyTuple_SetItem(p_read_from_file_args, 0, p_read_from_file_arg);
97+
auto p_timeline = PyObjectRef(PyObject_CallObject(p_read_from_file, p_read_from_file_args));
98+
99+
// Convert the Python timeline into a string and use that to create a C++ timeline.
100+
auto p_to_json_string = PyObjectRef(PyObject_GetAttrString(p_timeline, "to_json_string"));
101+
auto p_json_string = PyObjectRef(PyObject_CallObject(p_to_json_string, NULL));
102+
timeline = otio::SerializableObject::Retainer<otio::Timeline>(
103+
dynamic_cast<otio::Timeline*>(otio::Timeline::from_json_string(
104+
PyUnicode_AsUTF8AndSize(p_json_string, NULL),
105+
error_status)));
106+
}
107+
catch (const std::exception& e)
65108
{
66109
error_status->outcome = otio::ErrorStatus::Outcome::FILE_OPEN_FAILED;
67-
error_status->details = "cannot read file: " + file_name;
68-
return nullptr;
110+
error_status->details = e.what();
69111
}
70-
71-
// Read the timeline from the temporary JSON file.
72-
return dynamic_cast<otio::Timeline*>(otio::Timeline::from_json_file(temp_file_name, error_status));
112+
if (PyErr_Occurred())
113+
{
114+
PyErr_Print();
115+
}
116+
return timeline;
73117
}
74118

75119
bool PythonAdapters::write_to_file(
76120
otio::SerializableObject::Retainer<otio::Timeline> const& timeline,
77121
std::string const& file_name,
78122
otio::ErrorStatus* error_status)
79123
{
80-
// Write the timeline to a temporary JSON file.
81-
const std::string temp_file_name = create_temp_dir() + "/temp.otio";
82-
if (!timeline.value->to_json_file(temp_file_name, error_status))
124+
bool r = false;
125+
try
83126
{
84-
return false;
127+
// Import the OTIO Python module.
128+
auto p_module = PyObjectRef(PyImport_ImportModule("opentimelineio.adapters"));
129+
130+
// Convert the C++ timeline to a string and pass that to Python.
131+
const auto string = timeline.value->to_json_string(error_status);
132+
if (error_status->outcome != otio::ErrorStatus::Outcome::OK)
133+
{
134+
throw std::runtime_error("cannot convert to string");
135+
}
136+
auto p_read_from_string = PyObjectRef(PyObject_GetAttrString(p_module, "read_from_string"));
137+
auto p_read_from_string_args = PyObjectRef(PyTuple_New(1));
138+
auto p_read_from_string_arg = PyUnicode_FromStringAndSize(string.c_str(), string.size());
139+
if (!p_read_from_string_arg)
140+
{
141+
throw std::runtime_error("cannot create arg");
142+
}
143+
PyTuple_SetItem(p_read_from_string_args, 0, p_read_from_string_arg);
144+
auto p_timeline = PyObjectRef(PyObject_CallObject(p_read_from_string, p_read_from_string_args));
145+
146+
// Write the Python timeline.
147+
auto p_write_to_file = PyObjectRef(PyObject_GetAttrString(p_module, "write_to_file"));
148+
auto p_write_to_file_args = PyObjectRef(PyTuple_New(2));
149+
const std::string file_name_normalized = normalize_path(file_name);
150+
auto p_write_to_file_arg = PyUnicode_FromStringAndSize(file_name_normalized.c_str(), file_name_normalized.size());
151+
if (!p_write_to_file_arg)
152+
{
153+
throw std::runtime_error("cannot create arg");
154+
}
155+
PyTuple_SetItem(p_write_to_file_args, 0, p_timeline);
156+
PyTuple_SetItem(p_write_to_file_args, 1, p_write_to_file_arg);
157+
PyObjectRef(PyObject_CallObject(p_write_to_file, p_write_to_file_args));
158+
159+
r = true;
85160
}
86-
87-
// Convert the temporary JSON file to the output file.
88-
if (!_convert(temp_file_name, file_name))
161+
catch (const std::exception& e)
89162
{
90163
error_status->outcome = otio::ErrorStatus::Outcome::FILE_WRITE_FAILED;
91-
error_status->details = "cannot write file: " + file_name;
92-
return false;
164+
error_status->details = e.what();
93165
}
94-
95-
return true;
96-
}
97-
98-
bool PythonAdapters::_convert(const std::string& inFileName, const std::string& outFileName)
99-
{
100-
std::stringstream ss;
101-
ss << "import opentimelineio as otio\n";
102-
ss << "timeline = otio.adapters.read_from_file('" << normalize_path(inFileName) << "')\n";
103-
ss << "otio.adapters.write_to_file(timeline, '" << normalize_path(outFileName) << "')\n";
104-
return 0 == PyRun_SimpleString(ss.str().c_str());
166+
if (PyErr_Occurred())
167+
{
168+
PyErr_Print();
169+
}
170+
return r;
105171
}
106172

107173
int main(int argc, char** argv)

0 commit comments

Comments
 (0)