{{#args}}
  {{python_type}} {{name}}{{initialization}};
{{/args}}

{{#has_args}}
  if (!PyArg_ParseTuple(args, {{tuple_format}}))
  {
    PyErr_SetString(PyExc_TypeError, "Bad types for the arguments ({{count_args}} arguments expected)");
    return NULL;
  }
{{/has_args}}
{{#args}}{{#check_object_type}}
  if ({{name}} != Py_None && Py_TYPE({{name}}) != Get{{check_object_type}}Type())
  {
    PyErr_SetString(PyExc_TypeError, "Invalid orthanc.{{check_object_type}} object");
    return NULL;
  }
{{/check_object_type}}{{/args}}
{{#return_long}}
  long value;
  {
    PythonThreadsAllower allower;
    value = {{c_function}}(OrthancPlugins::GetGlobalContext(){{self}}{{call_args}});
  }
  {{#args}}{{release}}{{/args}}
  return PyLong_FromLong(value);
{{/return_long}}
{{#return_static_string}}
  const char* s;
  {
    PythonThreadsAllower allower;
    s = {{c_function}}(OrthancPlugins::GetGlobalContext(){{self}}{{call_args}});
  }
  {{#args}}{{release}}{{/args}}
  if (s == NULL)
  {
    Py_INCREF(Py_None);
    return Py_None;
  }
  else
  {
    return PyUnicode_FromString(s);
  }
{{/return_static_string}}
{{#return_dynamic_string}}
  OrthancPlugins::OrthancString s;
  {
    PythonThreadsAllower allower;
    s.Assign({{c_function}}(OrthancPlugins::GetGlobalContext(){{self}}{{call_args}}));
  }
  {{#args}}{{release}}{{/args}}
  if (s.GetContent() == NULL)
  {
    PythonLock::RaiseException(OrthancPluginErrorCode_InternalError);
    return NULL;
  }
  else
  {
    return PyUnicode_FromString(s.GetContent());
  }
{{/return_dynamic_string}}
{{#return_void}}
  {
    PythonThreadsAllower allower;
    {{c_function}}(OrthancPlugins::GetGlobalContext(){{self}}{{call_args}});
  }
  {{#args}}{{release}}{{/args}}

  Py_INCREF(Py_None);
  return Py_None;
{{/return_void}}
{{#return_error}}
  OrthancPluginErrorCode code;
  {
    PythonThreadsAllower allower;
    code = {{c_function}}(OrthancPlugins::GetGlobalContext(){{self}}{{call_args}});
  }
  {{#args}}{{release}}{{/args}}

  if (code == OrthancPluginErrorCode_Success)
  {
    Py_INCREF(Py_None);
    return Py_None;
  }
  else
  {
    PythonLock::RaiseException(code);
    return NULL;
  }
{{/return_error}}
{{#return_object}}
  // This is the case of a constructor
  {{return_object}}* obj;
  {
    PythonThreadsAllower allower;
    obj = {{c_function}}(OrthancPlugins::GetGlobalContext(){{self}}{{call_args}});
  }
  {{#args}}{{release}}{{/args}}
  if (obj == NULL)
  {
    PythonLock::RaiseException(OrthancPluginErrorCode_InternalError);
    return NULL;
  }
  else
  {
    PyObject *argList = Py_BuildValue("Lb", obj, false /* not borrowed */);
    PyObject *python = PyObject_CallObject((PyObject *) &sdk_{{return_object}}_Type, argList);
    Py_DECREF(argList);
    return python;
  }
{{/return_object}}
{{#return_bytes}}
  OrthancPlugins::MemoryBuffer buffer;
  OrthancPluginErrorCode code;
  {
    PythonThreadsAllower allower;
    code = {{c_function}}(OrthancPlugins::GetGlobalContext(), *buffer{{self}}{{call_args}});
  }
  {{#args}}{{release}}{{/args}}
  if (code == OrthancPluginErrorCode_Success)
  {
    return PyBytes_FromStringAndSize(reinterpret_cast<const char*>(buffer.GetData()), buffer.GetSize());
  }
  else
  {
    PythonLock::RaiseException(code);
    return NULL;  
  }
{{/return_bytes}}
{{#return_enumeration}}
  {{return_enumeration}} value;
  {
    PythonThreadsAllower allower;
    value = {{c_function}}(OrthancPlugins::GetGlobalContext(){{self}}{{call_args}});
  }
  {{#args}}{{release}}{{/args}}
  return PyLong_FromLong(value);
{{/return_enumeration}}
