/* -----------------------------------------------------------------------------
 * This file is part of SWIG, which is licensed as a whole under version 3
 * (or any later version) of the GNU General Public License. Some additional
 * terms also apply to certain portions of SWIG. The full details of the SWIG
 * license and copyrights can be found in the LICENSE and COPYRIGHT files
 * included with the SWIG source code as distributed by the SWIG developers
 * and at https://www.swig.org/legal.html.
 *
 * python.cxx
 *
 * Python language module for SWIG.
 * ----------------------------------------------------------------------------- */

#include "swigmod.h"
#include "cparse.h"
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include "pydoc.h"

#define PYSHADOW_MEMBER  0x2
#define WARN_PYTHON_MULTIPLE_INH 405

#define PYTHON_INT_MAX (2147483647)
#define PYTHON_INT_MIN (-2147483647-1)

static String *const_code = 0;
static String *module = 0;
static String *package = 0;
static String *mainmodule = 0;
static String *interface = 0;
static String *global_name = 0;
static int shadow = 1;
static int use_kw = 0;
static int director_method_index = 0;
static int builtin = 0;

static File *f_begin = 0;
static File *f_runtime = 0;
static File *f_runtime_h = 0;
static File *f_header = 0;
static File *f_wrappers = 0;
static File *f_directors = 0;
static File *f_directors_h = 0;
static File *f_init = 0;
static File *f_shadow_py = 0;
static String *f_shadow = 0;
static String *f_shadow_begin = 0;
static Hash *f_shadow_imports = 0;
static String *f_shadow_after_begin = 0;
static String *f_shadow_stubs = 0;
static Hash *builtin_getset = 0;
static Hash *builtin_closures = 0;
static Hash *class_members = 0;
static File *f_builtins = 0;
static String *builtin_tp_init = 0;
static String *builtin_methods = 0;
static String *builtin_default_unref = 0;
static String *builtin_closures_code = 0;
static String *f_varlinks = 0;

static String *methods;
static String *methods_proxydocs;
static String *class_name;
static String *shadow_indent = 0;
static int in_class = 0;
static int no_header_file = 0;
static int max_bases = 0;
static int builtin_bases_needed = 0;

/* C++ Support + Shadow Classes */

static int have_constructor = 0;
static int have_repr = 0;
static bool have_builtin_static_member_method_callback = false;
static bool have_fast_proxy_static_member_method_callback = false;
static String *real_classname;

/* Thread Support */
static int threads = 0;
static int nothreads = 0;

/* Other options */
static int dirvtable = 0;
static int doxygen = 0;
static int fastunpack = 1;
static int fastproxy = 0;
static int olddefs = 0;
static int castmode = 0;
static int extranative = 0;
static int nortti = 0;
static int relativeimport = 0;
static int flat_static_method = 0;
static int nogil = 0;

/* flags for the make_autodoc function */
namespace {
enum autodoc_t {
  AUTODOC_CLASS,
  AUTODOC_CTOR,
  AUTODOC_DTOR,
  AUTODOC_STATICFUNC,
  AUTODOC_FUNC,
  AUTODOC_METHOD,
  AUTODOC_CONST,
  AUTODOC_VAR
};
}

static const char *usage1 = "\
Python Options (available with -python)\n\
     -builtin        - Create Python built-in types rather than proxy classes, for better performance\n\
     -castmode       - Enable the casting mode, which allows implicit cast between types in Python\n\
     -debug-doxygen-parser     - Display doxygen parser module debugging information\n\
     -debug-doxygen-translator - Display doxygen translator module debugging information\n\
     -dirvtable      - Generate a pseudo virtual table for directors for faster dispatch\n\
     -doxygen        - Convert C++ doxygen comments to pydoc comments in proxy classes\n\
     -extranative    - Return extra native wrappers for C++ std containers wherever possible\n\
     -fastproxy      - Use fast proxy mechanism for member methods\n\
     -flatstaticmethod         - Generate additional flattened Python methods for C++ static methods\n\
     -globals <name> - Set <name> used to access C global variable (default: 'cvar')\n\
     -interface <mod>- Set low-level C/C++ module name to <mod> (default: module name prefixed by '_')\n\
     -keyword        - Use keyword arguments\n";
static const char *usage2 = "\
     -nofastunpack   - Use traditional UnpackTuple method to parse the argument functions\n\
     -nogil          - Enable free-threading if supported by the Python interpreter\n\
     -noh            - Don't generate the output header file\n";
static const char *usage3 = "\
     -noproxy        - Don't generate proxy classes\n\
     -nortti         - Disable the use of the native C++ RTTI with directors\n\
     -nothreads      - Disable thread support for the entire interface\n\
     -olddefs        - Keep the old method definitions when using -fastproxy\n\
     -relativeimport - Use relative Python imports\n\
     -threads        - Add thread support for all the interface\n\
     -O              - Enable the following optimization options:\n\
                         -fastdispatch -fastproxy -fvirtual\n\
\n";

static String *getSlot(Node *n = NULL, const char *key = NULL, String *default_slot = NULL) {
  static String *zero = NewString("0");
  String *val = n && key && *key ? Getattr(n, key) : NULL;
  return val ? val : default_slot ? default_slot : zero;
}

static String *getHeapTypesSlot(Node *n = NULL, const char *key = NULL, String *default_slot = NULL) {
  String *val = n && key && *key ? Getattr(n, key) : NULL;
  return val ? val : default_slot;
}

static void printSlot(File *f, String *slotval, const char *slotname, const char *functype = NULL) {
  String *slotval_override = 0;
  if (functype && Strcmp(slotval, "0") == 0)
    slotval = slotval_override = NewStringf("(%s) %s", functype, slotval);
  int len = Len(slotval);
  int fieldwidth = len >= 39 ? 1 : 39 - len;
  Printf(f, "    %s,%*s/* %s */\n", slotval, fieldwidth, "", slotname);
  Delete(slotval_override);
}

static void printHeapTypesSlot(File *f, String *slotval, const char *slotname, const char *functype = NULL) {
  if (slotval) {
    String *slotval_override = 0;
    if (functype)
      slotval = slotval_override = NewStringf("(%s) %s", functype, slotval);
    int len = Len(slotname);
    int fieldwidth = len >= 30 ? 1 : 30 - len;
    Printf(f, "    { Py_%s,%*s(void *)%s },\n", slotname, fieldwidth, "", slotval);
    Delete(slotval_override);
  }
}

static String *getClosure(String *functype, String *wrapper, int funpack = 0) {
  static const char *functypes[] = {
    "unaryfunc", "SWIGPY_UNARYFUNC_CLOSURE",
    "destructor", "SWIGPY_DESTRUCTOR_CLOSURE",
    "inquiry", "SWIGPY_INQUIRY_CLOSURE",
    "getiterfunc", "SWIGPY_GETITERFUNC_CLOSURE",
    "binaryfunc", "SWIGPY_BINARYFUNC_CLOSURE",
    "ternaryfunc", "SWIGPY_TERNARYFUNC_CLOSURE",
    "ternarycallfunc", "SWIGPY_TERNARYCALLFUNC_CLOSURE",
    "lenfunc", "SWIGPY_LENFUNC_CLOSURE",
    "ssizeargfunc", "SWIGPY_SSIZEARGFUNC_CLOSURE",
    "ssizessizeargfunc", "SWIGPY_SSIZESSIZEARGFUNC_CLOSURE",
    "ssizeobjargproc", "SWIGPY_SSIZEOBJARGPROC_CLOSURE",
    "ssizessizeobjargproc", "SWIGPY_SSIZESSIZEOBJARGPROC_CLOSURE",
    "objobjproc", "SWIGPY_OBJOBJPROC_CLOSURE",
    "objobjargproc", "SWIGPY_OBJOBJARGPROC_CLOSURE",
    "reprfunc", "SWIGPY_REPRFUNC_CLOSURE",
    "hashfunc", "SWIGPY_HASHFUNC_CLOSURE",
    "iternextfunc", "SWIGPY_ITERNEXTFUNC_CLOSURE",
    NULL
  };

  static const char *funpack_functypes[] = {
    "unaryfunc", "SWIGPY_UNARYFUNC_CLOSURE",
    "destructor", "SWIGPY_DESTRUCTOR_CLOSURE",
    "inquiry", "SWIGPY_INQUIRY_CLOSURE",
    "getiterfunc", "SWIGPY_GETITERFUNC_CLOSURE",
    "ternaryfunc", "SWIGPY_TERNARYFUNC_CLOSURE",
    "ternarycallfunc", "SWIGPY_TERNARYCALLFUNC_CLOSURE",
    "lenfunc", "SWIGPY_LENFUNC_CLOSURE",
    "ssizeargfunc", "SWIGPY_FUNPACK_SSIZEARGFUNC_CLOSURE",
    "ssizessizeargfunc", "SWIGPY_SSIZESSIZEARGFUNC_CLOSURE",
    "ssizeobjargproc", "SWIGPY_SSIZEOBJARGPROC_CLOSURE",
    "ssizessizeobjargproc", "SWIGPY_SSIZESSIZEOBJARGPROC_CLOSURE",
    "objobjproc", "SWIGPY_FUNPACK_OBJOBJPROC_CLOSURE",
    "objobjargproc", "SWIGPY_OBJOBJARGPROC_CLOSURE",
    "reprfunc", "SWIGPY_REPRFUNC_CLOSURE",
    "hashfunc", "SWIGPY_HASHFUNC_CLOSURE",
    "iternextfunc", "SWIGPY_ITERNEXTFUNC_CLOSURE",
    NULL
  };

  if (!functype)
    return NULL;
  char *c = Char(functype);
  int i;
  if (funpack) {
    for (i = 0; funpack_functypes[i] != NULL; i += 2) {
      if (!strcmp(c, funpack_functypes[i]))
	return NewStringf("%s(%s)", funpack_functypes[i + 1], wrapper);
    }
  } else {
    for (i = 0; functypes[i] != NULL; i += 2) {
      if (!strcmp(c, functypes[i]))
	return NewStringf("%s(%s)", functypes[i + 1], wrapper);
    }
  }
  return NULL;
}

class PYTHON:public Language {
public:
  PYTHON() {
    /* Add code to manage protected constructors and directors */
    director_prot_ctor_code = NewString("");
    Printv(director_prot_ctor_code,
	   "if ( $comparison ) { /* subclassed */\n",
	   "  $director_new \n",
	   "} else {\n", "  SWIG_SetErrorMsg(PyExc_RuntimeError,\"accessing abstract class or protected constructor\"); \n", "  SWIG_fail;\n", "}\n", NIL);
    director_multiple_inheritance = 1;
    directorLanguage();
  }

  ~PYTHON() {
    delete doxygenTranslator;
  }

  /* ------------------------------------------------------------
   * Thread Implementation
   * ------------------------------------------------------------ */
  int threads_enable(Node *n) const {
    return threads && !GetFlagAttr(n, "feature:nothread");
  }

  int initialize_threads(String *f_init) {
    if (!threads) {
      return SWIG_OK;
    }
    Printf(f_init, "\n");
    Printf(f_init, "/* Initialize threading */\n");
    Printf(f_init, "SWIG_PYTHON_INITIALIZE_THREADS;\n");

    return SWIG_OK;
  }

  virtual void thread_begin_block(Node *n, String *f) {
    if (!GetFlag(n, "feature:nothreadblock")) {
      String *bb = Getattr(n, "feature:threadbeginblock");
      if (bb) {
	Append(f, bb);
      } else {
	Append(f, "SWIG_PYTHON_THREAD_BEGIN_BLOCK;\n");
      }
    }
  }

  virtual void thread_end_block(Node *n, String *f) {
    if (!GetFlag(n, "feature:nothreadblock")) {
      String *eb = Getattr(n, "feature:threadendblock");
      if (eb) {
	Append(f, eb);
      } else {
	Append(f, "SWIG_PYTHON_THREAD_END_BLOCK;\n");
      }
    }
  }

  virtual void thread_begin_allow(Node *n, String *f) {
    if (!GetFlag(n, "feature:nothreadallow")) {
      String *bb = Getattr(n, "feature:threadbeginallow");
      Append(f, "{\n");
      if (bb) {
	Append(f, bb);
      } else {
	Append(f, "SWIG_PYTHON_THREAD_BEGIN_ALLOW;\n");
      }
    }
  }

  virtual void thread_end_allow(Node *n, String *f) {
    if (!GetFlag(n, "feature:nothreadallow")) {
      String *eb = Getattr(n, "feature:threadendallow");
      Append(f, "\n");
      if (eb) {
	Append(f, eb);
      } else {
	Append(f, "SWIG_PYTHON_THREAD_END_ALLOW;");
      }
      Append(f, "\n}");
    }
  }


  /* ------------------------------------------------------------
   * main()
   * ------------------------------------------------------------ */

  virtual void main(int argc, char *argv[]) {

    SWIG_library_directory("python");

    int doxygen_translator_flags = 0;

    for (int i = 1; i < argc; i++) {
      if (argv[i]) {
	if (strcmp(argv[i], "-interface") == 0) {
	  if (argv[i + 1]) {
	    interface = NewString(argv[i + 1]);
	    Swig_mark_arg(i);
	    Swig_mark_arg(i + 1);
	    i++;
	  } else {
	    Swig_arg_error();
	  }
	} else if (strcmp(argv[i], "-globals") == 0) {
	  if (argv[i + 1]) {
	    global_name = NewString(argv[i + 1]);
	    Swig_mark_arg(i);
	    Swig_mark_arg(i + 1);
	    i++;
	  } else {
	    Swig_arg_error();
	  }
	} else if ((strcmp(argv[i], "-shadow") == 0) || ((strcmp(argv[i], "-proxy") == 0))) {
	  shadow = 1;
	  Swig_mark_arg(i);
	} else if ((strcmp(argv[i], "-noproxy") == 0)) {
	  shadow = 0;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-keyword") == 0) {
	  use_kw = 1;
	  SWIG_cparse_set_compact_default_args(1);
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-nortti") == 0) {
	  nortti = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-threads") == 0) {
	  threads = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-nothreads") == 0) {
	  /* Turn off thread support mode */
	  nothreads = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-dirvtable") == 0) {
	  dirvtable = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-doxygen") == 0) {
	  doxygen = 1;
	  scan_doxygen_comments = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-debug-doxygen-translator") == 0) {
	  doxygen_translator_flags |= DoxygenTranslator::debug_translator;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-debug-doxygen-parser") == 0) {
	  doxygen_translator_flags |= DoxygenTranslator::debug_parser;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-nofastunpack") == 0) {
	  fastunpack = 0;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-fastproxy") == 0) {
	  fastproxy = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-olddefs") == 0) {
	  olddefs = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-castmode") == 0) {
	  castmode = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-extranative") == 0) {
	  extranative = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-flatstaticmethod") == 0) {
	  flat_static_method = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-noh") == 0) {
	  no_header_file = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-newvwm") == 0) {
	  /* Turn on new value wrapper mode */
	  /* Undocumented option, did have -help text: New value wrapper mode, use only when everything else fails */
	  Swig_value_wrapper_mode(1);
	  no_header_file = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-O") == 0) {
	  fastproxy = 1;
	  Wrapper_fast_dispatch_mode_set(1);
	  Wrapper_virtual_elimination_mode_set(1);
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-help") == 0) {
	  fputs(usage1, stdout);
	  fputs(usage2, stdout);
	  fputs(usage3, stdout);
	} else if (strcmp(argv[i], "-builtin") == 0) {
	  builtin = 1;
	  Preprocessor_define("SWIGPYTHON_BUILTIN", 0);
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-nogil") == 0) {
	  nogil = 1;
	  Preprocessor_define("SWIGPYTHON_NOGIL", 0);
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-relativeimport") == 0) {
	  relativeimport = 1;
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-cppcast") == 0 ||
		   strcmp(argv[i], "-fastinit") == 0 ||
		   strcmp(argv[i], "-fastquery") == 0 ||
		   strcmp(argv[i], "-fastunpack") == 0 ||
		   strcmp(argv[i], "-modern") == 0 ||
		   strcmp(argv[i], "-modernargs") == 0 ||
		   strcmp(argv[i], "-noproxydel") == 0 ||
		   strcmp(argv[i], "-safecstrings") == 0) {
	  Printf(stderr, "Deprecated command line option: %s. Ignored, this option is now always on.\n", argv[i]);
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-py3") == 0) {
	  Printf(stderr, "Deprecated command line option: %s. Ignored, this option is no longer supported.\n", argv[i]);
	  Swig_mark_arg(i);
	} else if (strcmp(argv[i], "-aliasobj0") == 0 ||
		   strcmp(argv[i], "-buildnone") == 0 ||
		   strcmp(argv[i], "-classic") == 0 ||
		   strcmp(argv[i], "-classptr") == 0 ||
		   strcmp(argv[i], "-new_repr") == 0 ||
		   strcmp(argv[i], "-new_vwm") == 0 ||
		   strcmp(argv[i], "-newrepr") == 0 ||
		   strcmp(argv[i], "-noaliasobj0") == 0 ||
		   strcmp(argv[i], "-nobuildnone") == 0 ||
		   strcmp(argv[i], "-nocastmode") == 0 ||
		   strcmp(argv[i], "-nocppcast") == 0 ||
		   strcmp(argv[i], "-nodirvtable") == 0 ||
		   strcmp(argv[i], "-noextranative") == 0 ||
		   strcmp(argv[i], "-nofastinit") == 0 ||
		   strcmp(argv[i], "-nofastproxy") == 0 ||
		   strcmp(argv[i], "-nofastquery") == 0 ||
		   strcmp(argv[i], "-nomodern") == 0 ||
		   strcmp(argv[i], "-nomodernargs") == 0 ||
		   strcmp(argv[i], "-noolddefs") == 0 ||
		   strcmp(argv[i], "-nooutputtuple") == 0 ||
		   strcmp(argv[i], "-noproxyimport") == 0 ||
		   strcmp(argv[i], "-nosafecstrings") == 0 ||
		   strcmp(argv[i], "-old_repr") == 0 ||
		   strcmp(argv[i], "-oldrepr") == 0 ||
		   strcmp(argv[i], "-outputtuple") == 0 ||
		   strcmp(argv[i], "-proxydel") == 0) {
	  Printf(stderr, "Deprecated command line option: %s. This option is no longer available.\n", argv[i]);
	  Swig_mark_arg(i);
	  Exit(EXIT_FAILURE);
	}

      }
    }

    if (builtin && !shadow) {
      Printf(stderr, "Incompatible options -builtin and -noproxy specified.\n");
      Exit(EXIT_FAILURE);
    }

    if (fastproxy) {
      Preprocessor_define("SWIGPYTHON_FASTPROXY", 0);
    }

    if (doxygen)
      doxygenTranslator = new PyDocConverter(doxygen_translator_flags);

    if (!global_name)
      global_name = NewString("cvar");
    Preprocessor_define("SWIGPYTHON 1", 0);
    SWIG_config_file("python.swg");
    allow_overloading();
  }


  /* ------------------------------------------------------------
   * top()
   * ------------------------------------------------------------ */

  virtual int top(Node *n) {
    /* check if directors are enabled for this module.  note: this
     * is a "master" switch, without which no director code will be
     * emitted.  %feature("director") statements are also required
     * to enable directors for individual classes or methods.
     *
     * use %module(directors="1") modulename at the start of the
     * interface file to enable director generation.
     */
    String *mod_docstring = NULL;
    String *moduleimport = NULL;
    {
      Node *mod = Getattr(n, "module");
      if (mod) {
	Node *options = Getattr(mod, "options");
	if (options) {
	  int dirprot = 0;
	  if (Getattr(options, "dirprot")) {
	    dirprot = 1;
	  }
	  if (Getattr(options, "nodirprot")) {
	    dirprot = 0;
	  }
	  if (Getattr(options, "directors")) {
	    allow_directors();
	    if (dirprot)
	      allow_dirprot();
	  }
	  if (Getattr(options, "threads")) {
	    threads = 1;
	  }
	  if (Getattr(options, "castmode")) {
	    castmode = 1;
	  }
	  if (Getattr(options, "nocastmode")) {
	    Printf(stderr, "Deprecated module option: %s. This option is no longer supported.\n", "nocastmode");
	    Exit(EXIT_FAILURE);
	  }
	  if (Getattr(options, "extranative")) {
	    extranative = 1;
	  }
	  if (Getattr(options, "noextranative")) {
	    Printf(stderr, "Deprecated module option: %s. This option is no longer supported.\n", "noextranative");
	    Exit(EXIT_FAILURE);
	  }
	  if (Getattr(options, "outputtuple")) {
	    Printf(stderr, "Deprecated module option: %s. This option is no longer supported.\n", "outputtuple");
	    Exit(EXIT_FAILURE);
	  }
	  if (Getattr(options, "nooutputtuple")) {
	    Printf(stderr, "Deprecated module option: %s. This option is no longer supported.\n", "nooutputtuple");
	    Exit(EXIT_FAILURE);
	  }
	  mod_docstring = Getattr(options, "docstring");
	  package = Getattr(options, "package");
	  moduleimport = Getattr(options, "moduleimport");
	}
      }
    }

    /* Set comparison with none for ConstructorToFunction */
    setSubclassInstanceCheck(NewString("$arg != Py_None"));

    /* Initialize all of the output files */
    String *outfile = Getattr(n, "outfile");
    String *outfile_h = !no_header_file ? Getattr(n, "outfile_h") : 0;

    f_begin = NewFile(outfile, "w", SWIG_output_files());
    if (!f_begin) {
      FileErrorDisplay(outfile);
      Exit(EXIT_FAILURE);
    }
    f_runtime = NewString("");
    f_init = NewString("");
    f_header = NewString("");
    f_wrappers = NewString("");
    f_directors_h = NewString("");
    f_directors = NewString("");
    builtin_getset = NewHash();
    builtin_closures = NewHash();
    builtin_closures_code = NewString("");
    class_members = NewHash();
    builtin_methods = NewString("");
    builtin_default_unref = NewString("delete $self;");
    f_varlinks = NewString("");

    if (builtin) {
      f_builtins = NewString("");
    }

    if (Swig_directors_enabled()) {
      if (!no_header_file) {
	f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
	if (!f_runtime_h) {
	  FileErrorDisplay(outfile_h);
	  Exit(EXIT_FAILURE);
	}
      } else {
	f_runtime_h = f_runtime;
      }
    }

    /* Register file targets with the SWIG file handler */
    Swig_register_filebyname("header", f_header);
    Swig_register_filebyname("wrapper", f_wrappers);
    Swig_register_filebyname("begin", f_begin);
    Swig_register_filebyname("runtime", f_runtime);
    Swig_register_filebyname("init", f_init);
    Swig_register_filebyname("director", f_directors);
    Swig_register_filebyname("director_h", f_directors_h);

    const_code = NewString("");
    methods = NewString("");
    methods_proxydocs = NewString("");

    Swig_banner(f_begin);

    Swig_obligatory_macros(f_runtime, "PYTHON");

    if (Swig_directors_enabled()) {
      Printf(f_runtime, "#define SWIG_DIRECTORS\n");
    }

    if (nothreads) {
      Printf(f_runtime, "#define SWIG_PYTHON_NO_THREADS\n");
    } else if (threads) {
      Printf(f_runtime, "#define SWIG_PYTHON_THREADS\n");
    }

    if (!dirvtable) {
      Printf(f_runtime, "#define SWIG_PYTHON_DIRECTOR_NO_VTABLE\n");
    }

    if (nortti) {
      Printf(f_runtime, "#ifndef SWIG_DIRECTOR_NORTTI\n");
      Printf(f_runtime, "#define SWIG_DIRECTOR_NORTTI\n");
      Printf(f_runtime, "#endif\n");
    }

    if (castmode) {
      Printf(f_runtime, "#define SWIG_CASTRANK_MODE\n");
      Printf(f_runtime, "#define SWIG_PYTHON_CAST_MODE\n");
    }

    if (extranative) {
      Printf(f_runtime, "#define SWIG_PYTHON_EXTRA_NATIVE_CONTAINERS\n");
    }

    if (builtin) {
      Printf(f_runtime, "#define SWIGPYTHON_BUILTIN\n");
    }

    if (fastproxy) {
      Printf(f_runtime, "#define SWIGPYTHON_FASTPROXY\n");
    }

    if (nogil) {
      Printf(f_runtime, "#define SWIGPYTHON_NOGIL\n");
    }

    Printf(f_runtime, "\n");

    Printf(f_header, "#ifdef SWIG_TypeQuery\n");
    Printf(f_header, "# undef SWIG_TypeQuery\n");
    Printf(f_header, "#endif\n");
    Printf(f_header, "#define SWIG_TypeQuery SWIG_Python_TypeQuery\n");


    /* Set module name */
    module = Copy(Getattr(n, "name"));
    mainmodule = Getattr(n, "name");

    if (Swig_directors_enabled()) {
      Swig_banner(f_directors_h);
      Printf(f_directors_h, "\n");
      Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module);
      Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module);
      if (dirprot_mode()) {
	Printf(f_directors_h, "#include <map>\n");
	Printf(f_directors_h, "#include <string>\n\n");
      }

      Printf(f_directors, "\n\n");
      Printf(f_directors, "/* ---------------------------------------------------\n");
      Printf(f_directors, " * C++ director class methods\n");
      Printf(f_directors, " * --------------------------------------------------- */\n\n");
      if (outfile_h) {
	String *filename = Swig_file_filename(outfile_h);
	Printf(f_directors, "#include \"%s\"\n\n", filename);
	Delete(filename);
      }
    }

    /* If shadow classing is enabled, we're going to change the module name to "_module" */
    String *default_import_code = NewString("");
    if (shadow) {
      String *filen = NewStringf("%s%s.py", SWIG_output_directory(), Char(module));
      // If we don't have an interface then change the module name X to _X
      if (interface)
	module = interface;
      else
	Insert(module, 0, "_");
      if ((f_shadow_py = NewFile(filen, "w", SWIG_output_files())) == 0) {
	FileErrorDisplay(filen);
	Exit(EXIT_FAILURE);
      }
      Delete(filen);
      filen = NULL;

      f_shadow = NewString("");
      f_shadow_begin = NewString("");
      f_shadow_imports = NewHash();
      f_shadow_after_begin = NewString("");
      f_shadow_stubs = NewString("");

      Swig_register_filebyname("shadow", f_shadow);
      Swig_register_filebyname("python", f_shadow);

      if (!builtin) {
	/* Import the low-level C/C++ module.  This should be a relative import,
	 * since the shadow module may also have been imported by a relative
	 * import, and there is thus no guarantee that the low-level C/C++ module is on
	 * sys.path.  Relative imports must be explicitly specified from 2.6.0
	 * onwards (implicit relative imports raised a DeprecationWarning in 2.6,
	 * and fail in 2.7 onwards).
	 *
	 * First check for __spec__.parent which is available from 3.4 onwards,
	 * see https://docs.python.org/3/reference/import.html#spec. If not,
	 * check for __package__, which is available from 2.6 onwards (see PEP366),
	 * but will no longer be set/used in 3.15.
	 * Next try determine the shadow wrapper's package based on the __name__ it
	 * was given by the importer that loaded it.
	 * If the module is in a package, load the low-level C/C++ module from the
	 * same package, otherwise load it as a global module.
	 */
        Printv(default_import_code, "# Import the low-level C/C++ module\n", NULL);
        Printv(default_import_code, "if getattr(globals().get(\"__spec__\"), \"parent\", None) or __package__ or \".\" in __name__:\n", NULL);
        Printv(default_import_code, tab4, "from . import ", module, "\n", NULL);
        Printv(default_import_code, "else:\n", NULL);
        Printv(default_import_code, tab4, "import ", module, "\n", NULL);
      } else {
        Printv(default_import_code, "# Pull in all the attributes from the low-level C/C++ module\n", NULL);
        Printv(default_import_code, "if getattr(globals().get(\"__spec__\"), \"parent\", None) or __package__ or \".\" in __name__:\n", NULL);
        Printv(default_import_code, tab4, "from .", module, " import *\n", NULL);
        Printv(default_import_code, "else:\n", NULL);
        Printv(default_import_code, tab4, "from ", module, " import *\n", NULL);
      }

      if (!builtin) {
	/* Need builtins to qualify names like Exception that might also be
	   defined in this module (try both Python 3 and Python 2 names) */
	Printv(f_shadow, "try:\n", tab4, "import builtins as __builtin__\n", "except ImportError:\n", tab4, "import __builtin__\n", NULL);
      }

      if (!builtin && fastproxy) {
	Printf(f_shadow, "\n");
	Printf(f_shadow, "_swig_new_instance_method = %s.SWIG_PyInstanceMethod_New\n", module);
	Printf(f_shadow, "_swig_new_static_method = %s.SWIG_PyStaticMethod_New\n", module);
      }

      if (!builtin) {
	Printv(f_shadow, "\n",
	       "def _swig_repr(self):\n",
	       tab4, "try:\n",
	       tab4, tab4, "strthis = \"proxy of \" + self.this.__repr__()\n",
	       tab4, "except __builtin__.Exception:\n",
	       tab4, tab4, "strthis = \"\"\n",
	       tab4, "return \"<%s.%s; %s >\" % (self.__class__.__module__, self.__class__.__name__, strthis,)\n\n", NIL);

	Printv(f_shadow, "\n",
	       "def _swig_setattr_nondynamic_instance_variable(set):\n",
	       tab4, "def set_instance_attr(self, name, value):\n",
	       tab4, tab4, "if name == \"this\":\n",
	       tab4, tab4, tab4, "set(self, name, value)\n",
	       tab4, tab4, "elif name == \"thisown\":\n",
	       tab4, tab4, tab4, "self.this.own(value)\n",
	       tab4, tab4, "elif hasattr(self, name) and isinstance(getattr(type(self), name), property):\n",
	       tab4, tab4, tab4, "set(self, name, value)\n",
	       tab4, tab4, "else:\n",
	       tab4, tab4, tab4, "raise AttributeError(\"You cannot add instance attributes to %s\" % self)\n",
	       tab4, "return set_instance_attr\n\n", NIL);

	Printv(f_shadow, "\n",
	       "def _swig_setattr_nondynamic_class_variable(set):\n",
	       tab4, "def set_class_attr(cls, name, value):\n",
	       tab4, tab4, "if hasattr(cls, name) and not isinstance(getattr(cls, name), property):\n",
	       tab4, tab4, tab4, "set(cls, name, value)\n",
	       tab4, tab4, "else:\n",
	       tab4, tab4, tab4, "raise AttributeError(\"You cannot add class attributes to %s\" % cls)\n",
	       tab4, "return set_class_attr\n\n", NIL);

	Printv(f_shadow, "\n",
	       "def _swig_add_metaclass(metaclass):\n",
	       tab4, "\"\"\"Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass\"\"\"\n",
	       tab4, "def wrapper(cls):\n",
	       tab4, tab4, "return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy())\n",
	       tab4, "return wrapper\n\n", NIL);

	Printv(f_shadow, "\n",
	       "class _SwigNonDynamicMeta(type):\n",
	       tab4, "\"\"\"Meta class to enforce nondynamic attributes (no new attributes) for a class\"\"\"\n",
	       tab4, "__setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__)\n",
	       "\n", NIL);

	Printv(f_shadow, "\n", NIL);

	if (Swig_directors_enabled())
	  Printv(f_shadow, "import weakref\n\n", NIL);
      }
    }
    // Include some information in the code
    Printf(f_header, "\n/*-----------------------------------------------\n              @(target):= %s.so\n\
  ------------------------------------------------*/\n", module);

    Printf(f_header, "#if PY_VERSION_HEX >= 0x03000000\n");
    Printf(f_header, "#  define SWIG_init    PyInit_%s\n\n", module);
    Printf(f_header, "#else\n");
    Printf(f_header, "#  define SWIG_init    init%s\n\n", module);
    Printf(f_header, "#endif\n");
    Printf(f_runtime, "#define SWIG_name    \"%s\"\n", module);

    Printf(f_wrappers, "#ifdef __cplusplus\n");
    Printf(f_wrappers, "extern \"C\" {\n");
    Printf(f_wrappers, "#endif\n");
    Append(const_code, "static swig_const_info swig_const_table[] = {\n");
    Append(methods, "static PyMethodDef SwigMethods[] = {\n");
    Append(methods_proxydocs, "static PyMethodDef SwigMethods_proxydocs[] = {\n");

    /* the method exported for replacement of new.instancemethod in Python 3 */
    add_pyinstancemethod_new();
    add_pystaticmethod_new();

    if (builtin) {
      SwigType *s = NewString("SwigPyObject");
      SwigType_add_pointer(s);
      SwigType_remember(s);
      Delete(s);
    }

    /* emit code */
    Language::top(n);

    if (Swig_directors_enabled()) {
      // Insert director runtime into the f_runtime file (make it occur before %header section)
      Swig_insert_file("director_common.swg", f_runtime);
      Swig_insert_file("director_py_mutex.swg", f_runtime);
      Swig_insert_file("director_guard.swg", f_runtime);
      Swig_insert_file("director.swg", f_runtime);
    }

    /* Close language module */
    Append(methods, "\t { NULL, NULL, 0, NULL }\n");
    Append(methods, "};\n");
    Printf(f_wrappers, "%s\n", methods);
    Append(methods_proxydocs, "\t { NULL, NULL, 0, NULL }\n");
    Append(methods_proxydocs, "};\n");
    if ((fastproxy && !builtin) || have_fast_proxy_static_member_method_callback)
      Printf(f_wrappers, "%s\n", methods_proxydocs);

    if (builtin) {
      Dump(f_builtins, f_wrappers);
    }

    SwigType_emit_type_table(f_runtime, f_wrappers);

    Append(const_code, "{0, 0, 0, 0.0, 0, 0}};\n");
    Printf(f_wrappers, "%s\n", const_code);

    if (have_fast_proxy_static_member_method_callback)
      Printf(f_init, "  SWIG_Python_FixMethods(SwigMethods_proxydocs, swig_const_table, swig_types, swig_type_initial);\n\n");

    initialize_threads(f_init);

    Dump(f_varlinks, f_init);

    Printf(f_init, "  return 0;\n");
    Printf(f_init, "}\n");

    Printf(f_wrappers, "#ifdef __cplusplus\n");
    Printf(f_wrappers, "}\n");
    Printf(f_wrappers, "#endif\n");

    if (shadow) {
      Swig_banner_target_lang(f_shadow_py, "#");

      if (mod_docstring) {
	if (Len(mod_docstring)) {
	  const char *triple_double = "\"\"\"";
	  // follow PEP257 rules: https://www.python.org/dev/peps/pep-0257/
	  // reported by pep257: https://github.com/GreenSteam/pep257
	  bool multi_line_ds = Strchr(mod_docstring, '\n') != 0;
	  Printv(f_shadow_py, "\n", triple_double, multi_line_ds ? "\n":"", mod_docstring, multi_line_ds ? "\n":"", triple_double, "\n", NIL);
	}
	Delete(mod_docstring);
	mod_docstring = NULL;
      }

      if (Len(f_shadow_begin) > 0)
	Printv(f_shadow_py, "\n", f_shadow_begin, "\n", NIL);

      Printv(f_shadow_py, "\nfrom sys import version_info as _swig_python_version_info\n", NULL);

      if (Len(f_shadow_after_begin) > 0)
	Printv(f_shadow_py, f_shadow_after_begin, "\n", NIL);

      if (moduleimport) {
	Replaceall(moduleimport, "$module", module);
	Printv(f_shadow_py, moduleimport, "\n", NIL);
      } else {
	Printv(f_shadow_py, default_import_code, NIL);
      }

      if (Len(f_shadow) > 0)
	Printv(f_shadow_py, "\n", f_shadow, "\n", NIL);
      if (Len(f_shadow_stubs) > 0)
	Printv(f_shadow_py, f_shadow_stubs, "\n", NIL);
      Delete(f_shadow_py);
    }

    /* Close all of the files */
    Dump(f_runtime, f_begin);
    Dump(f_header, f_begin);

    if (Swig_directors_enabled()) {
      Dump(f_directors_h, f_runtime_h);
      Printf(f_runtime_h, "\n");
      Printf(f_runtime_h, "#endif\n");
      if (f_runtime_h != f_begin)
	Delete(f_runtime_h);
      Dump(f_directors, f_begin);
    }

    Dump(f_wrappers, f_begin);
    if (builtin && builtin_bases_needed)
      Printf(f_begin, "static PyTypeObject *builtin_bases[%d];\n\n", max_bases + 2);
    Wrapper_pretty_print(f_init, f_begin);

    Delete(default_import_code);
    Delete(f_shadow_after_begin);
    Delete(f_shadow_imports);
    Delete(f_shadow_begin);
    Delete(f_shadow);
    Delete(f_header);
    Delete(f_wrappers);
    Delete(f_builtins);
    Delete(f_init);
    Delete(f_directors);
    Delete(f_directors_h);
    Delete(f_runtime);
    Delete(f_begin);
    Delete(f_varlinks);

    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * Emit the wrapper for PyInstanceMethod_New to MethodDef array.
   * This wrapper is used to implement -fastproxy,
   * as a replacement of new.instancemethod in Python 3.
   * ------------------------------------------------------------ */
  int add_pyinstancemethod_new() {
    if (!builtin && fastproxy) {
      String *name = NewString("SWIG_PyInstanceMethod_New");
      String *line = NewString("");
      Printf(line, "\t { \"%s\", %s, METH_O, NULL},\n", name, name);
      Append(methods, line);
      Append(methods_proxydocs, line);
      Delete(line);
      Delete(name);
    }
    return 0;
  }

  /* ------------------------------------------------------------
   * Emit the wrapper for PyStaticMethod_New to MethodDef array.
   * This wrapper is used to ensure the correct documentation is
   * generated for static methods when using -fastproxy
   * ------------------------------------------------------------ */
  int add_pystaticmethod_new() {
    if (!builtin && fastproxy) {
      String *name = NewString("SWIG_PyStaticMethod_New");
      String *line = NewString("");
      Printf(line, "\t { \"%s\", %s, METH_O, NULL},\n", name, name);
      Append(methods, line);
      Append(methods_proxydocs, line);
      Delete(line);
      Delete(name);
    }
    return 0;
  }

  /* ------------------------------------------------------------
   * subpkg_tail()
   *
   * Return the name of 'other' package relative to 'base'.
   *
   * 1. If 'other' is a sub-package of 'base', returns the 'other' relative to
   *    'base'.
   * 2. If 'other' and 'base' are equal, returns empty string "".
   * 3. In any other case, NULL pointer is returned.
   *
   * The 'base' and 'other' are expected to be fully qualified names.
   *
   * NOTE: none of 'base' nor 'other' can be null.
   *
   * Examples:
   *
   *  #  base       other         tail
   * --  ----       -----         ----
   *  1  "Foo"      "Foo.Bar" ->  "Bar"
   *  2	 "Foo"      "Foo."    ->  ""
   *  3	 "Foo"      "FooB.ar" ->  NULL
   *  4	 "Foo.Bar"  "Foo.Bar" ->  ""
   *  5  "Foo.Bar"  "Foo"     ->  NULL
   *  6  "Foo.Bar"  "Foo.Gez" ->  NULL
   *
   *  NOTE: the example #2 is actually a syntax error (at input). I believe
   *        swig parser prevents us from this case happening here.
   * ------------------------------------------------------------ */

  static String *subpkg_tail(const String *base, const String *other) {
    int baselen = Len(base);
    int otherlen = Len(other);

    if (Strncmp(other, base, baselen) == 0) {
      if ((baselen < otherlen) && (Char(other))[baselen] == '.') {
        return NewString((Char(other)) + baselen + 1);
      } else if (baselen == otherlen) {
        return NewString("");
      } else {
        return 0;
      }
    } else {
      return 0;
    }
  }

  /* ------------------------------------------------------------
   * abs_import_directive_string()
   *
   * Return a string containing python code to import module.
   *
   * 	pkg     package name or the module being imported
   * 	mod     module name of the module being imported
   * 	pfx     optional prefix to module name
   *
   * NOTE: keep this function consistent with abs_import_name_string().
   * ------------------------------------------------------------ */

  static String *abs_import_directive_string(const String *pkg, const String *mod, const char *pfx = "") {
    String *out = NewString("");

    if (pkg && *Char(pkg)) {
      Printf(out, "import %s.%s%s\n", pkg, pfx, mod);
    } else {
      Printf(out, "import %s%s\n", pfx, mod);
    }
    return out;
  }

  /* ------------------------------------------------------------
   * rel_import_directive_string()
   *
   * Return a string containing python code to import module that
   * is potentially within a package.
   *
   * 	mainpkg	package name of the module which imports the other module
   * 	pkg     package name or the module being imported
   * 	mod     module name of the module being imported
   * 	pfx     optional prefix to module name
   *
   * NOTE: keep this function consistent with rel_import_name_string().
   * ------------------------------------------------------------ */

  static String *rel_import_directive_string(const String *mainpkg, const String *pkg, const String *mod, const char *pfx = "") {

    /* NOTE: things are not so trivial. This is what we do here (by examples):
     *
     * 0. To import module 'foo', which is not in any package, we do absolute
     *    import:
     *
     *       import foo
     *
     * 1. To import 'pkg1.pkg2.foo', when mainpkg != "pkg1" and
     *    mainpkg != "pkg1.pkg2" or when mainpkg is not given we do absolute
     *    import:
     *
     *          import pkg1.pkg2.foo
     *
     * 2. To import module pkg1.foo, when mainpkg == "pkg1", we do:
     *
     *    - for py3 = 0:
     *
     *          import foo
     *
     *    - for py3 = 1:
     *
     *          from . import foo
     *
     * 3. To import "pkg1.pkg2.pkg3.foo", when mainpkg = "pkg1", we do:
     *
     *    - for py3 == 0:
     *
     *          import pkg2.pkg3.foo
     *
     *    - for py3 == 1:
     *
     *          from . import pkg2  # [1]
     *          import pkg1.pkg2.pkg3.foo
     *
     * NOTE: [1] is necessary for pkg2.foo to be present in the importing module
     */

    String *apkg = 0; // absolute (FQDN) package name of pkg
    String *rpkg = 0; // relative package name
    int py3_rlen1 = 0; // length of 1st level sub-package name, used by py3
    String *out = NewString("");

    if (pkg && *Char(pkg)) {
      if (mainpkg) {
	String *tail = subpkg_tail(mainpkg, pkg);
	if (tail) {
	  if (*Char(tail)) {
	    rpkg = NewString(tail);
	    const char *py3_end1 = Strchr(rpkg, '.');
	    if (!py3_end1)
	      py3_end1 = (Char(rpkg)) + Len(rpkg);
	    py3_rlen1 = (int)(py3_end1 - Char(rpkg));
	  } else {
	    rpkg = NewString("");
	  }
	  Delete(tail);
	} else {
	  apkg = NewString(pkg);
	}
      } else {
	apkg = NewString(pkg);
      }
    } else {
      apkg = NewString("");
    }

    if (apkg) {
      Printf(out, "import %s%s%s%s\n", apkg, *Char(apkg) ? "." : "", pfx, mod);
      Delete(apkg);
    } else {
      if (py3_rlen1)
	Printf(out, "from . import %.*s\n", py3_rlen1, rpkg);
      Printf(out, "from .%s import %s%s\n", rpkg, pfx, mod);
      Delete(rpkg);
    }
    return out;
  }

  /* ------------------------------------------------------------
   * import_directive_string()
   * ------------------------------------------------------------ */

  static String *import_directive_string(const String *mainpkg, const String *pkg, const String *mod, const char *pfx = "") {
    if (!relativeimport) {
      return abs_import_directive_string(pkg, mod, pfx);
    } else {
      return rel_import_directive_string(mainpkg, pkg, mod, pfx);
    }
  }

  /* ------------------------------------------------------------
   * abs_import_name_string()
   *
   * Return a string with the name of a symbol (perhaps imported
   * from external module by absolute import directive).
   *
   * mainpkg  package name of current module
   * mainmod  module name of current module
   * pkg      package name of (perhaps other) module
   * mod      module name of (perhaps other) module
   * sym      symbol name
   *
   * NOTE: mainmod, mod, and sym can't be NULL.
   * NOTE: keep this function consistent with abs_import_directive_string()
   * ------------------------------------------------------------ */

  static String *abs_import_name_string(const String *mainpkg, const String *mainmod, const String *pkg, const String *mod, const String *sym) {
    String *out = NewString("");
    if (pkg && *Char(pkg)) {
      if (mainpkg && *Char(mainpkg)) {
        if (Strcmp(mainpkg,pkg) != 0 || Strcmp(mainmod, mod) != 0) {
          Printf(out, "%s.%s.", pkg, mod);
        }
      } else {
        Printf(out, "%s.%s.", pkg, mod);
      }
    } else if ((mainpkg && *Char(mainpkg)) || Strcmp(mainmod, mod) != 0) {
      Printf(out, "%s.", mod);
    }
    Append(out, sym);
    return out;
  }

  /* ------------------------------------------------------------
   * rel_import_name_string()
   *
   * Return a string with the name of a symbol (perhaps imported
   * from external module by relative import directive).
   *
   * mainpkg  package name of current module
   * mainmod  module name of current module
   * pkg      package name of (perhaps other) module
   * mod      module name of (perhaps other) module
   * sym      symbol name
   *
   * NOTE: mainmod, mod, and sym can't be NULL.
   * NOTE: keep this function consistent with rel_import_directive_string()
   * ------------------------------------------------------------ */

  static String *rel_import_name_string(const String *mainpkg, const String *mainmod, const String *pkg, const String *mod, const String *sym) {
    String *out = NewString("");
    if (pkg && *Char(pkg)) {
      String *tail = 0;
      if (mainpkg)
        tail = subpkg_tail(mainpkg, pkg);
      if (!tail)
        tail = NewString(pkg);
      if (*Char(tail)) {
        Printf(out, "%s.%s.", tail, mod);
      } else if (Strcmp(mainmod, mod) != 0) {
        Printf(out, "%s.", mod);
      }
      Delete(tail);
    } else if ((mainpkg && *Char(mainpkg)) || Strcmp(mainmod, mod) != 0) {
      Printf(out, "%s.", mod);
    }
    Append(out, sym);
    return out;
  }

  /* ------------------------------------------------------------
   * import_name_string()
   * ------------------------------------------------------------ */

  static String *import_name_string(const String *mainpkg, const String *mainmod, const String *pkg, const String *mod, const String *sym) {
    if (!relativeimport) {
      return abs_import_name_string(mainpkg,mainmod,pkg,mod,sym);
    } else {
      return rel_import_name_string(mainpkg,mainmod,pkg,mod,sym);
    }
  }

  /* ------------------------------------------------------------
   * importDirective()
   * ------------------------------------------------------------ */

  virtual int importDirective(Node *n) {
    if (shadow) {
      String *modname = Getattr(n, "module");

      if (modname) {
	// Find the module node for this imported module.  It should be the
	// first child but search just in case.
	Node *mod = firstChild(n);
	while (mod && Strcmp(nodeType(mod), "module") != 0)
	  mod = nextSibling(mod);

	Node *options = Getattr(mod, "options");
	String *pkg = options ? Getattr(options, "package") : 0;

	if (!options || (!Getattr(options, "noshadow") && !Getattr(options, "noproxy"))) {
	  String *_import = import_directive_string(package, pkg, modname, "_");
	  if (!GetFlagAttr(f_shadow_imports, _import)) {
	    String *import = import_directive_string(package, pkg, modname);
	    Printf(builtin ? f_shadow_after_begin : f_shadow, "%s", import);
	    Delete(import);
	    SetFlag(f_shadow_imports, _import);
	  }
	  Delete(_import);
	}

      }
    }
    return Language::importDirective(n);
  }

  /* ------------------------------------------------------------
   * funcCall()
   *
   * Emit shadow code to call a function in the extension
   * module. Using proper argument and calling style for
   * given node n.
   * ------------------------------------------------------------ */
  String *funcCall(String *name, String *parms) {
    String *str = NewString("");

    Printv(str, module, ".", name, "(", parms, ")", NIL);
    return str;
  }

  /* ------------------------------------------------------------
   * indent_pythoncode()
   *
   * Format (indent) Python code.
   * Remove leading whitespace from 'code' and re-indent using
   * the indentation string in 'indent'.
   * ------------------------------------------------------------ */

  String *indent_pythoncode(const String *code, const_String_or_char_ptr indent, String *file, int line, const char *directive_name) {
    String *out = NewString("");
    String *temp;
    char *t;
    if (!indent)
      indent = "";

    temp = NewString(code);

    t = Char(temp);
    if (*t == '{') {
      Delitem(temp, 0);
      Delitem(temp, DOH_END);
    }

    /* Split the input text into lines */
    List *clist = SplitLines(temp);
    Delete(temp);

    // Line number within the pythoncode.
    int py_line = 0;

    String *initial = 0;
    Iterator si;

    /* Get the initial indentation.  Skip lines which only contain whitespace
     * and/or a comment, as the indentation of those doesn't matter:
     *
     *     A logical line that contains only spaces, tabs, formfeeds and
     *     possibly a comment, is ignored (i.e., no NEWLINE token is
     *     generated).
     *
     * see:
     * https://docs.python.org/2/reference/lexical_analysis.html#blank-lines
     * https://docs.python.org/3/reference/lexical_analysis.html#blank-lines
     */
    for (si = First(clist); si.item; si = Next(si), ++py_line) {
      const char *c = Char(si.item);
      int i;
      for (i = 0; isspace((unsigned char)c[i]); i++) {
	// Scan forward until we find a non-space (which may be a null byte).
      }
      char ch = c[i];
      if (ch && ch != '#') {
	// Found a line with actual content.
	initial = NewStringWithSize(c, i);
	break;
      }
      if (ch) {
	Printv(out, indent, c, NIL);
      }
      Putc('\n', out);
    }

    // Process remaining lines.
    for ( ; si.item; si = Next(si), ++py_line) {
      const char *c = Char(si.item);
      // If no prefixed line was found, the above loop should have completed.
      assert(initial);

      int i;
      for (i = 0; isspace((unsigned char)c[i]); i++) {
	// Scan forward until we find a non-space (which may be a null byte).
      }
      char ch = c[i];
      if (!ch) {
	// Line is just whitespace - emit an empty line.
	Putc('\n', out);
	continue;
      }

      if (ch == '#') {
	// Comment - the indentation doesn't matter to python, but try to
	// adjust the whitespace for the benefit of human readers (though SWIG
	// currently seems to always remove any whitespace before a '#' before
	// we get here, in which case we'll just leave the comment at the start
	// of the line).
	if (i >= Len(initial)) {
	  Printv(out, indent, NIL);
	}

	Printv(out, c + i, "\n", NIL);
	continue;
      }

      if (i < Len(initial)) {
	// There's non-whitespace in the initial prefix of this line.
	Swig_error(file, line, "Line indented less than expected (line %d of %s) as no line should be indented less than the indentation in line 1\n", py_line, directive_name);
	Printv(out, indent, c, "\n", NIL);
      } else {
	if (memcmp(c, Char(initial), Len(initial)) == 0) {
	  // Prefix matches initial, so just remove it.
	  Printv(out, indent, c + Len(initial), "\n", NIL);
	  continue;
	}
	Swig_warning(WARN_PYTHON_INDENT_MISMATCH,
		     file, line, "Whitespace indentation is inconsistent compared to earlier lines (line %d of %s)\n", py_line, directive_name);
	// To avoid gratuitously breaking interface files which worked with
	// SWIG <= 3.0.5, we remove a prefix of the same number of bytes for
	// lines which start with different whitespace to the line we got
	// 'initial' from.
	Printv(out, indent, c + Len(initial), "\n", NIL);
      }
    }
    Delete(clist);
    return out;
  }

  /* ------------------------------------------------------------
   * indent_docstring()
   *
   * Format (indent) a Python docstring.
   * Remove leading whitespace from 'code' and re-indent using
   * the indentation string in 'indent'.
   * ------------------------------------------------------------ */

  String *indent_docstring(const String *code, const char *indent) {
    String *out = NewString("");
    String *temp;
    char *t;
    if (!indent)
      indent = "";

    temp = NewString(code);

    t = Char(temp);
    if (*t == '{') {
      Delitem(temp, 0);
      Delitem(temp, DOH_END);
    }

    /* Split the input text into lines */
    List *clist = SplitLines(temp);
    Delete(temp);

    Iterator si;

    int truncate_characters_count = INT_MAX;
    for (si = First(clist); si.item; si = Next(si)) {
      const char *c = Char(si.item);
      int i;
      for (i = 0; isspace((unsigned char)c[i]); i++) {
	// Scan forward until we find a non-space (which may be a null byte).
      }
      char ch = c[i];
      if (ch) {
	// Found a line which isn't just whitespace
	if (i < truncate_characters_count)
	  truncate_characters_count = i;
      }
    }

    if (truncate_characters_count == INT_MAX)
      truncate_characters_count = 0;

    for (si = First(clist); si.item; si = Next(si)) {
      const char *c = Char(si.item);

      int i;
      for (i = 0; isspace((unsigned char)c[i]); i++) {
	// Scan forward until we find a non-space (which may be a null byte).
      }
      char ch = c[i];
      if (!ch) {
	// Line is just whitespace - emit an empty line.
	Putc('\n', out);
	continue;
      }

      Printv(out, indent, c + truncate_characters_count, "\n", NIL);
    }
    Delete(clist);
    return out;
  }

  /* ------------------------------------------------------------
   * autodoc level declarations
   * ------------------------------------------------------------ */

  enum autodoc_l {
    NO_AUTODOC = -2,		// no autodoc
    STRING_AUTODOC = -1,	// use provided string
    NAMES_AUTODOC = 0,		// only parameter names
    TYPES_AUTODOC = 1,		// parameter names and types
    EXTEND_AUTODOC = 2,		// extended documentation and parameter names
    EXTEND_TYPES_AUTODOC = 3	// extended documentation and parameter types + names
  };


  autodoc_l autodoc_level(String *autodoc) {
    autodoc_l dlevel = NO_AUTODOC;
    char *c = Char(autodoc);
    if (c) {
      if (isdigit(c[0])) {
	dlevel = (autodoc_l) atoi(c);
      } else {
	if (strcmp(c, "extended") == 0) {
	  dlevel = EXTEND_AUTODOC;
	} else {
	  dlevel = STRING_AUTODOC;
	}
      }
    }
    return dlevel;
  }


  /* ------------------------------------------------------------
   * have_docstring()
   *
   * Check if there is a docstring directive and it has text,
   * or there is an autodoc flag set
   * ------------------------------------------------------------ */

  bool have_docstring(Node *n) {
    String *str = Getattr(n, "feature:docstring");
    return ((str && Len(str) > 0)
	|| (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"))
	|| (doxygen && doxygenTranslator->hasDocumentation(n))
      );
  }

  /* ------------------------------------------------------------
   * find_overload_with_docstring()
   *
   * This function should be called with the node pointing to the
   * last element of an overload set and returns an overload with
   * a docstring or null if there are none.
   *
   * The idea is that, because the Python docstring is shared by
   * all overloads, it's this function return value and not the
   * node itself which needs to be passes to docstring() later.
   * ------------------------------------------------------------ */
  Node *find_overload_with_docstring(Node *n) {
    for (Node *node_with_doc = n; node_with_doc; node_with_doc = Getattr(node_with_doc, "sym:previousSibling")) {
      if (have_docstring(node_with_doc))
	return node_with_doc;
    }

    return NULL;
  }

  /* ------------------------------------------------------------
   * build_combined_docstring()
   *
   * Build the full docstring:
   * Use the docstring if there is one present otherwise
   * use the Doxygen comment if there is one present.
   * Ignore autodoc if there is a Doxygen comment, otherwise
   * create the autodoc string and append to any docstring.
   *
   * Return new string to be deleted by caller (never NIL but
   * may be empty if there is no docstring).
   * ------------------------------------------------------------ */

  String *build_combined_docstring(Node *n, autodoc_t ad_type, const char *indent = "", bool low_level = false) {
    bool add_autodoc = true;
    String *docstr = Getattr(n, "feature:docstring");
    if (docstr) {
      // Simplify the code below by just ignoring empty docstrings.
      if (!Len(docstr))
	docstr = NULL;
      else
	docstr = Copy(docstr);
    }

    if (docstr) {
      char *t = Char(docstr);
      if (*t == '{') {
	Delitem(docstr, 0);
	Delitem(docstr, DOH_END);
      }
    }

    if (!docstr) {
      if (doxygen && doxygenTranslator->hasDocumentation(n)) {
	docstr = Getattr(n, "python:docstring");
	if (!docstr) {
	  docstr = doxygenTranslator->getDocumentation(n, 0);

	  // Avoid rebuilding it again the next time: notice that we can't do
	  // this for the combined doc string as autodoc part of it depends on
	  // the sym:name of the node and it is changed while handling it, so
	  // the cached results become incorrect. But Doxygen docstring only
	  // depends on the comment which is not going to change, so we can
	  // safely cache it.
	  Setattr(n, "python:docstring", Copy(docstr));
	} else {
	  // Must copy here since if the docstring is multi-line, the String*
	  // here will get Deleted below, which is bad if it is a pointer to
	  // the cached object!
	  docstr = Copy(docstr);
	}
	add_autodoc = false;
      }
    }

    if (add_autodoc && Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc")) {
      String *autodoc = make_autodoc(n, ad_type, low_level);
      if (autodoc && Len(autodoc) > 0) {
	if (docstr) {
	  Append(autodoc, "\n");
	  Append(autodoc, docstr);
	}

	String *tmp = autodoc;
	autodoc = docstr;
	docstr = tmp;
      }

      Delete(autodoc);
    }

    if (!docstr)
      docstr = NewString("");

    // If there is more than one line then make docstrings like this:
    //
    //      """
    //      This is line1
    //      And here is line2 followed by the rest of them
    //      """
    //
    // otherwise, put it all on a single line
    if (Strchr(docstr, '\n')) {
      String *tmp = NewString("");
      Append(tmp, "\n");
      Append(tmp, indent_docstring(docstr, indent));
      Append(tmp, indent);
      Delete(docstr);
      docstr = tmp;
    } else {
      // Removing leading and trailing whitespace for single line docstrings
      Chop(docstr);
      const char *c = Char(docstr);
      if (isspace((int)*c)) {
	while(isspace((int)*(++c))) {
	}
	String *old_docstr = docstr;
	docstr = NewString(c);
	Delete(old_docstr);
      }
    }

    return docstr;
  }

  /* ------------------------------------------------------------
   * docstring()
   *
   * Get the docstring text, stripping off {} if necessary,
   * and enclose in triple double quotes.  If autodoc is also
   * set then it will build a combined docstring.
   * ------------------------------------------------------------ */

  String *docstring(Node *n, autodoc_t ad_type, const char *indent, bool low_level = false) {
    String *docstr = build_combined_docstring(n, ad_type, indent, low_level);
    const int len = Len(docstr);
    if (!len)
      return docstr;

    // Notice that all comments are created as raw strings (prefix "r"),
    // because '\' is used often in comments, but may break Python module from
    // loading. For example, in doxy comment one may write path in quotes:
    //
    //     This is path to file "C:\x\file.txt"
    //
    // Python will not load the module with such comment because of illegal
    // escape '\x'. '\' may additionally appear in verbatim or htmlonly sections
    // of doxygen doc, Latex expressions, ...
    String *doc = NewString("");

    // Determine which kind of quotes to use as delimiters: for single line
    // strings we can avoid problems with having a quote as the last character
    // of the docstring by using different kind of quotes as delimiters. For
    // multi-line strings this problem doesn't arise, as we always have a new
    // line or spaces at the end of it, but it still does no harm to do it for
    // them too.
    //
    // Note: we use double quotes by default, i.e. if there is no reason to
    // prefer using single ones, for consistency with the older SWIG versions.
    const bool useSingleQuotes = (Char(docstr))[len - 1] == '"';

    Append(doc, useSingleQuotes ? "r'''" : "r\"\"\"");

    // We also need to avoid having triple quotes of whichever type we use, as
    // this would break Python doc string syntax too. Unfortunately there is no
    // way to have triple quotes inside of raw-triple-quoted string, so we have
    // to break the string in parts and rely on concatenation of the adjacent
    // string literals.
    if (useSingleQuotes)
      Replaceall(docstr, "'''", "''' \"'''\" '''");
    else
      Replaceall(docstr, "\"\"\"", "\"\"\" '\"\"\"' \"\"\"");

    Append(doc, docstr);
    Append(doc, useSingleQuotes ? "'''" : "\"\"\"");
    Delete(docstr);

    return doc;
  }

  /* ------------------------------------------------------------
   * cdocstring()
   *
   * Get the docstring text as it would appear in C-language
   * source code (but without quotes around it).
   * ------------------------------------------------------------ */

  String *cdocstring(Node *n, autodoc_t ad_type, bool low_level = false) {
    String *ds = build_combined_docstring(n, ad_type, "", low_level);
    Replaceall(ds, "\\", "\\\\");
    Replaceall(ds, "\"", "\\\"");
    Replaceall(ds, "\n", "\\n\"\n\t\t\"");
    return ds;
  }

  /* -----------------------------------------------------------------------------
   * addMissingParameterNames()
   *
   * For functions that have not had nameless parameters set in the Language class.
   *
   * Inputs:
   *   plist - entire parameter list
   *   arg_num - the number to start from when naming arguments
   * Side effects:
   *   The "lname" attribute in each parameter in plist will be contain a parameter name
   * ----------------------------------------------------------------------------- */

  void addMissingParameterNames(Node *n, ParmList *plist, int arg_num) {
    Parm *p = plist;
    int i = arg_num;
    while (p) {
      if (!Getattr(p, "lname")) {
	String *name = makeParameterName(n, p, i);
	Setattr(p, "lname", name);
	Delete(name);
      }
      i++;
      p = nextSibling(p);
    }
  }

  /* ------------------------------------------------------------
   * make_autodocParmList()
   *
   * Generate the documentation for the function parameters
   * Parameters:
   *    arg_num:         The number to start assigning unnamed arguments from
   *    func_annotation: Function annotation support
   * ------------------------------------------------------------ */

  String *make_autodocParmList(Node *n, bool showTypes, int arg_num = 1, bool calling = false, bool func_annotation = false) {

    String *doc = NewString("");
    String *pdocs = 0;
    ParmList *plist = CopyParmList(Getattr(n, "parms"));
    Parm *p;
    Parm *pnext;

    if (calling)
      func_annotation = false;

    addMissingParameterNames(n, plist, arg_num); // for $1_name substitutions done in Swig_typemap_attach_parms
    Swig_typemap_attach_parms("in", plist, 0);
    Swig_typemap_attach_parms("doc", plist, 0);

    if (Strcmp(ParmList_protostr(plist), "void") == 0) {
      //No parameters actually
      return doc;
    }

    for (p = plist; p; p = pnext) {
      String *tm = Getattr(p, "tmap:in");
      if (tm) {
	pnext = Getattr(p, "tmap:in:next");
	if (checkAttribute(p, "tmap:in:numinputs", "0")) {
	  continue;
	}
      } else {
	pnext = nextSibling(p);
      }

      String *name = 0;
      String *type = 0;
      String *value = 0;
      String *pdoc = Getattr(p, "tmap:doc");
      if (pdoc) {
	name = Getattr(p, "tmap:doc:name");
	type = Getattr(p, "tmap:doc:type");
	value = Getattr(p, "tmap:doc:value");
      }

      // Skip the "self" argument - it is added to the parameter list automatically
      // and shouldn't be included in the Parameters block
      if (Getattr(p, "self")) {
	continue;
      }

      // Note: the generated name should be consistent with that in kwnames[]
      String *made_name = 0;
      if (!name) {
	name = made_name = makeParameterName(n, p, arg_num);
      }

      // Increment the argument number once we are sure this is a real argument to count
      arg_num++;

      type = type ? type : Getattr(p, "type");
      value = value ? value : Getattr(p, "value");

      if (SwigType_isvarargs(type)) {
	Delete(made_name);
	break;
      }

      if (Len(doc)) {
	// add a comma to the previous one if any
	Append(doc, ", ");
      }

      // Do the param type too?
      Node *nn = classLookup(Getattr(p, "type"));
      String *type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0);
      if (showTypes)
	Printf(doc, "%s ", type_str);

      Append(doc, name);
      if (pdoc) {
	if (!pdocs)
	  // numpydoc style: https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt
	  pdocs = NewString("\nParameters\n----------\n");
	Printf(pdocs, "%s\n", pdoc);
      }
      // Write the function annotation
      if (func_annotation)
	Printf(doc, ": \"%s\"", type_str);

      // Write default value
      if (value && !calling) {
	String *new_value = convertValue(value, Getattr(p, "numval"), Getattr(p, "stringval"), Getattr(p, "type"));
	if (new_value) {
	  value = new_value;
	} else {
	  // Even if the value is not representable in the target language, still use it in the documentation, for compatibility with the previous SWIG versions
	  // and because it can still be useful to see the C++ expression there.
	  Node *lookup = Swig_symbol_clookup(value, 0);
	  if (lookup)
	    value = Getattr(lookup, "sym:name");
	}
	Printf(doc, "=%s", value);

	if (new_value)
	  Delete(new_value);
      }
      Delete(type_str);
      Delete(made_name);
    }
    if (pdocs)
      Setattr(n, "feature:pdocs", pdocs);
    Delete(plist);
    return doc;
  }

  /* ------------------------------------------------------------
   * make_autodoc()
   *
   * Build a docstring for the node, using parameter and other
   * info in the parse tree.  If the value of the autodoc
   * attribute is "0" then do not include parameter types, if
   * it is "1" (the default) then do.  If it has some other
   * value then assume it is supplied by the extension writer
   * and use it directly.
   * ------------------------------------------------------------ */

  String *make_autodoc(Node *n, autodoc_t ad_type, bool low_level = false) {
    int extended = 0;
    bool first_func = true;
    // If the function is overloaded then this function is called
    // for the last one.  Rewind to the first so the docstrings are
    // in order.
    while (Getattr(n, "sym:previousSibling"))
      n = Getattr(n, "sym:previousSibling");

    String *doc = NewString("");
    while (n) {
      bool showTypes = false;
      bool skipAuto = false;
      String *autodoc = Getattr(n, "feature:autodoc");
      autodoc_l dlevel = autodoc_level(autodoc);
      switch (dlevel) {
      case NO_AUTODOC:
	break;
      case NAMES_AUTODOC:
	showTypes = false;
	break;
      case TYPES_AUTODOC:
	showTypes = true;
	break;
      case EXTEND_AUTODOC:
	extended = 1;
	showTypes = false;
	break;
      case EXTEND_TYPES_AUTODOC:
	extended = 1;
	showTypes = true;
	break;
      case STRING_AUTODOC:
	Append(doc, autodoc);
	skipAuto = true;
	break;
      }

      if (!skipAuto) {
	/* Check if a documentation name was given for either the low-level C API or high-level Python shadow API */
	String *symname = Getattr(n, low_level ? "doc:low:name" : "doc:high:name");
	if (!symname) {
	  symname = Getattr(n, "sym:name");
	}

	SwigType *type = Getattr(n, "type");
	String *type_str = NULL;

	// If the function has default arguments, then that documentation covers this version too
	if (Getattr(n, "defaultargs") != NULL) {
	  n = Getattr(n, "sym:nextSibling");
	  continue;
	}

	if (!first_func)
	  Append(doc, "\n");

	if (type) {
	  if (Strcmp(type, "void") == 0) {
	    type_str = NULL;
	  } else {
	    Node *nn = classLookup(type);
	    type_str = nn ? Copy(Getattr(nn, "sym:name")) : SwigType_str(type, 0);
	  }
	}

	/* Treat the low-level C API functions for getting/setting variables as methods for documentation purposes */
	String *kind = Getattr(n, "kind");
	if (kind && Strcmp(kind, "variable") == 0) {
	  if (ad_type == AUTODOC_FUNC) {
	    ad_type = AUTODOC_METHOD;
	  }
	}
	/* Treat destructors as methods for documentation purposes */
	String *nodeType = Getattr(n, "nodeType");
	if (nodeType && Strcmp(nodeType, "destructor") == 0) {
	  if (ad_type == AUTODOC_FUNC) {
	    ad_type = AUTODOC_METHOD;
	  }
	}

	switch (ad_type) {
	case AUTODOC_CLASS:
	  {
	    // Only do the autodoc if there isn't a docstring for the class
	    String *str = Getattr(n, "feature:docstring");
	    if (!str || Len(str) == 0) {
	      if (builtin) {
		SwigType *name = Getattr(n, "name");
		SwigType *sname = add_explicit_scope(name);
		String *rname = SwigType_namestr(sname);
		Printf(doc, "%s", rname);
		Delete(sname);
		Delete(rname);
	      } else {
		String *classname_str = SwigType_namestr(real_classname);
		if (CPlusPlus) {
		  Printf(doc, "Proxy of C++ %s class.", classname_str);
		} else {
		  Printf(doc, "Proxy of C %s struct.", classname_str);
		}
		Delete(classname_str);
	      }
	    }
	  }
	  break;
	case AUTODOC_CTOR:
	  if (Strcmp(class_name, symname) == 0) {
	    String *paramList = make_autodocParmList(n, showTypes, 2);
	    Printf(doc, "__init__(");
	    if (showTypes)
	      Printf(doc, "%s ", class_name);
	    if (Len(paramList))
	      Printf(doc, "self, %s) -> %s", paramList, class_name);
	    else
	      Printf(doc, "self) -> %s", class_name);
	  } else
	    Printf(doc, "%s(%s) -> %s", symname, make_autodocParmList(n, showTypes), class_name);
	  break;

	case AUTODOC_DTOR:
	  if (showTypes)
	    Printf(doc, "__del__(%s self)", class_name);
	  else
	    Printf(doc, "__del__(self)");
	  break;

	case AUTODOC_STATICFUNC:
	  Printf(doc, "%s(%s)", symname, make_autodocParmList(n, showTypes));
	  if (type_str)
	    Printf(doc, " -> %s", type_str);
	  break;

	case AUTODOC_FUNC:
	  Printf(doc, "%s(%s)", symname, make_autodocParmList(n, showTypes));
	  if (type_str)
	    Printf(doc, " -> %s", type_str);
	  break;

	case AUTODOC_METHOD:
	  {
	    String *paramList = make_autodocParmList(n, showTypes, 2);
	    Printf(doc, "%s(", symname);
	    if (showTypes)
	      Printf(doc, "%s ", class_name);
	    if (Len(paramList))
	      Printf(doc, "self, %s)", paramList);
	    else
	      Printf(doc, "self)");
	    if (type_str)
	      Printf(doc, " -> %s", type_str);
	  }
	  break;

	case AUTODOC_CONST:
	  // There is no autodoc support for constants currently, this enum
	  // element only exists to allow calling docstring() with it.
	  return NULL;
	case AUTODOC_VAR:
	  // Variables can also be documented (e.g. through the property() function in python)
	  Printf(doc, "%s", symname);
	  if (showTypes) {
	    String *type = Getattr(n, "tmap:doc:type");
	    if (!type)
	      type = Getattr(n, "membervariableHandler:type");
	    if (!type)
	      type = Getattr(n, "type");
	    Printf(doc, " : %s", type);
	  }
	  break;
	}
	Delete(type_str);

	// Special case: wrapper functions to get a variable should have no parameters.
	// Because the node is re-used for the setter and getter, the feature:pdocs field will
	// exist for the getter function, so explicitly avoid printing parameters in this case.
	bool variable_getter = kind && Strcmp(kind, "variable") == 0 && Getattr(n, "memberget");
	if (extended && ad_type != AUTODOC_VAR && !variable_getter) {
	  String *pdocs = Getattr(n, "feature:pdocs");
	  if (pdocs) {
	    Printv(doc, "\n", pdocs, NULL);
	  }
	}
      }
      // if it's overloaded then get the next decl and loop around again
      n = Getattr(n, "sym:nextSibling");
      if (n)
	first_func = false;
    }

    return doc;
  }

  /* ------------------------------------------------------------
   * convertIntegerValue()
   *
   * Check if string v is an integer and can be represented in
   * Python. If so, return an appropriate Python representation,
   * otherwise (or if we are unsure), return NIL.
   * ------------------------------------------------------------ */
  String *convertIntegerValue(String *v, SwigType *resolved_type) {
    const char *const s = Char(v);
    char *end;

    // Check if this is an integer number in any base.
    errno = 0;
    long value = strtol(s, &end, 0);
    if (errno == ERANGE || end == s || *end != '\0') {
      return NIL;
    }
    // So now we are certain that we are indeed dealing with an integer
    // that has a representation as long given by value.

    // Restrict to guaranteed supported range in Python, see maxint docs: https://docs.python.org/2/library/sys.html#sys.maxint
    // Don't do this pointless check when long is 32 bits or smaller as strtol will have already failed with ERANGE
#if LONG_MAX > PYTHON_INT_MAX || LONG_MIN < PYTHON_INT_MIN
    if (value > PYTHON_INT_MAX || value < PYTHON_INT_MIN) {
      return NIL;
    }
#endif

    if (Equal(resolved_type, "bool"))
      // Allow integers as the default value for a bool parameter.
      return NewString(value ? "True" : "False");

    if (value == 0)
      return NewString(SwigType_ispointer(resolved_type) ? "None" : "0");

    return Copy(v);
  }

  /* ------------------------------------------------------------
   * convertDoubleValue()
   *
   * Check if the given string looks like a decimal floating point constant
   * and return it if it does, otherwise return NIL.
   * ------------------------------------------------------------ */
  String *convertDoubleValue(String *v) {
    const char *const s = Char(v);
    char *end;

    errno = 0;
    double value = strtod(s, &end);
    (void) value;
    if (errno != ERANGE && end != s) {
      // An added complication: at least some versions of strtod() recognize
      // hexadecimal floating point numbers which don't exist in Python, so
      // detect them ourselves and refuse to convert them (this can't be done
      // without loss of precision in general).
      //
      // Also don't accept neither "NAN" nor "INFINITY" (both of which
      // conveniently contain "n").
      if (strpbrk(s, "xXnN"))
	return NIL;

      // Disregard optional "f" suffix, it can be just dropped in Python as it
      // uses doubles for everything anyhow.
      for (char * p = end; *p != '\0'; ++p) {
	switch (*p) {
	  case 'f':
	  case 'F':
	    break;

	  default:
	    return NIL;
	}
      }

      // Avoid unnecessary string allocation in the common case when we don't
      // need to remove any suffix.
      return *end == '\0' ? Copy(v) : NewStringWithSize(s, (int)(end - s));
    }

    return NIL;
  }

  /* ------------------------------------------------------------
   * convertValue()
   *
   * Check if string v can be a Python value literal or a
   * constant. Return an equivalent Python representation,
   * or NIL if it isn't, or we are unsure.
   * ------------------------------------------------------------ */
  String *convertValue(String *v, String *numval, String *stringval, SwigType *type) {
    if (stringval) {
      return NIL;
      // FIXME: This needs more careful testing.
      // return NewStringf("'%(escape)s'", stringval);
    }
    SwigType *resolved_type = SwigType_typedef_resolve_all(type);
    SwigType *unqualified_type = NIL;
    if (SwigType_isreference(resolved_type)) {
	SwigType *t = Copy(resolved_type);
	t = SwigType_del_reference(t);
	unqualified_type = SwigType_strip_qualifiers(t);
	Delete(t);
    } else {
	unqualified_type = SwigType_strip_qualifiers(resolved_type);
    }
    if (numval) {
      if (Equal(unqualified_type, "bool")) {
	Delete(resolved_type);
	Delete(unqualified_type);
	return NewString(*Char(numval) == '0' ? "False" : "True");
      }
      String *result = convertIntegerValue(numval, unqualified_type);
      Delete(resolved_type);
      Delete(unqualified_type);
      return result;
    }

    String *result = convertDoubleValue(v);
    if (!result) {
      if (Strcmp(v, "NULL") == 0 || Strcmp(v, "nullptr") == 0)
	result = SwigType_ispointer(unqualified_type) ? NewString("None") : NewString("0");
      // This could also be an enum type, default value of which could be
      // representable in Python if it doesn't include any scope (which could,
      // but currently is not, translated).
      else if (!Strchr(v, ':')) {
	Node *lookup = Swig_symbol_clookup(v, 0);
	if (lookup) {
	  if (Cmp(Getattr(lookup, "nodeType"), "enumitem") == 0)
	    result = Copy(Getattr(lookup, "sym:name"));
	}
      }
    }

    Delete(resolved_type);
    Delete(unqualified_type);
    return result;
  }

  /* ------------------------------------------------------------
   * is_representable_as_pyargs()
   *
   * Check if the function parameters default argument values
   * can be represented in Python.
   *
   * If this method returns false, the parameters will be translated
   * to a generic "*args" which allows us to deal with default values
   * at C++ code level where they can always be handled.
   * ------------------------------------------------------------ */
  bool is_representable_as_pyargs(Node *n) {
    ParmList *plist = CopyParmList(Getattr(n, "parms"));
    Swig_typemap_attach_parms("default", plist, NULL);

    Parm *p;
    Parm *pnext;

    for (p = plist; p; p = pnext) {
      pnext = nextSibling(p);
      String *tm = Getattr(p, "tmap:in");
      if (tm) {
	Parm *in_next = Getattr(p, "tmap:in:next");
	if (in_next)
	  pnext = in_next;
	if (checkAttribute(p, "tmap:in:numinputs", "0")) {
	  continue;
	}
      }

      // "default" typemap can contain arbitrary C++ code, so while it could, in
      // principle, be possible to examine it and check if it's just something
      // simple of the form "$1 = expression" and then use convertValue() to
      // check if expression can be used in Python, but for now we just
      // pessimistically give up and prefer to handle this at C++ level only.
      if (Getattr(p, "tmap:default"))
	return false;

      String *value = Getattr(p, "value");
      if (value) {
	String *convertedValue = convertValue(value, Getattr(p, "numval"), Getattr(p, "stringval"), Getattr(p, "type"));
	if (!convertedValue)
	  return false;
	Delete(convertedValue);
      }
    }

    return true;
  }


  /* ------------------------------------------------------------
   * is_real_overloaded()
   *
   * Check if the function is overloaded, but not just have some
   * siblings generated due to the original function having
   * default arguments.
   * ------------------------------------------------------------ */
  bool is_real_overloaded(Node *n) {
    Node *h = Getattr(n, "sym:overloaded");
    Node *i;
    if (!h)
      return false;

    i = Getattr(h, "sym:nextSibling");
    while (i) {
      Node *nn = Getattr(i, "defaultargs");
      if (nn != h) {
	/* Check if overloaded function has defaultargs and
	 * pointed to the first overloaded. */
	return true;
      }
      i = Getattr(i, "sym:nextSibling");
    }

    return false;
  }

  /* ------------------------------------------------------------
   * make_pyParmList()
   *
   * Generate parameter list for Python functions or methods,
   * reuse make_autodocParmList() to do so.
   * ------------------------------------------------------------ */
  String *make_pyParmList(Node *n, bool in_class, bool is_calling, int kw, bool has_self_for_count = false) {
    /* Get the original function for a defaultargs copy,
     * see default_arguments() in parser.y. */
    Node *nn = Getattr(n, "defaultargs");
    if (nn)
      n = nn;

    Parm *parms = Getattr(n, "parms");
    int varargs = parms ? emit_isvarargs(parms) : 0;

    /* We prefer to explicitly list all parameters of the C function in the
       generated Python code as this makes the function more convenient to use,
       however in some cases we must replace the real parameters list with just
       the catch all "*args". This happens when:

	1. The function is overloaded as Python doesn't support this.
	2. We were explicitly asked to use the "compact" arguments form.
	3. We were explicitly asked to use default args from C via the "python:cdefaultargs" feature.
	4. One of the default argument values can't be represented in Python.
	5. Varargs that haven't been forced to use a fixed number of arguments with %varargs.
     */
    if (is_real_overloaded(n) || GetFlag(n, "feature:compactdefaultargs") || GetFlag(n, "feature:python:cdefaultargs") || !is_representable_as_pyargs(n) || varargs) {
      String *parms = NewString("");
      if (in_class)
	Printf(parms, "self, ");
      Printf(parms, "*args");
      if (kw)
	Printf(parms, ", **kwargs");
      return parms;
    }

    bool funcanno = Equal(Getattr(n, "feature:python:annotations"), "c") ? true : false;
    String *params = NewString("");
    String *_params = make_autodocParmList(n, false, ((in_class || has_self_for_count)? 2 : 1), is_calling, funcanno);

    if (in_class) {
      Printf(params, "self");
      if (Len(_params) > 0)
	Printf(params, ", ");
    }

    Printv(params, _params, NULL);

    return params;
  }

  /* ------------------------------------------------------------
   * have_pythonprepend()
   *
   * Check if there is a %pythonprepend directive and it has text
   * ------------------------------------------------------------ */

  bool have_pythonprepend(Node *n) {
    String *str = Getattr(n, "feature:pythonprepend");
    return (str && Len(str) > 0);
  }

  /* ------------------------------------------------------------
   * pythonprepend()
   *
   * Get the %pythonprepend code, stripping off {} if necessary
   * ------------------------------------------------------------ */

  String *pythonprepend(Node *n) {
    String *str = Getattr(n, "feature:pythonprepend");
    char *t = Char(str);
    if (*t == '{') {
      Delitem(str, 0);
      Delitem(str, DOH_END);
    }
    return str;
  }

  /* ------------------------------------------------------------
   * have_pythonappend()
   *
   * Check if there is a %pythonappend directive and it has text
   * ------------------------------------------------------------ */

  bool have_pythonappend(Node *n) {
    String *str = Getattr(n, "feature:pythonappend");
    if (!str)
      str = Getattr(n, "feature:addtofunc");
    return (str && Len(str) > 0);
  }

  /* ------------------------------------------------------------
   * pythonappend()
   *
   * Get the %pythonappend code, stripping off {} if necessary
   * ------------------------------------------------------------ */

  String *pythonappend(Node *n) {
    String *str = Getattr(n, "feature:pythonappend");
    if (!str)
      str = Getattr(n, "feature:addtofunc");

    char *t = Char(str);
    if (*t == '{') {
      Delitem(str, 0);
      Delitem(str, DOH_END);
    }
    return str;
  }

  /* ------------------------------------------------------------
   * have_addtofunc()
   *
   * Check if there is a %addtofunc directive and it has text
   * ------------------------------------------------------------ */

  bool have_addtofunc(Node *n) {
    return have_pythonappend(n) || have_pythonprepend(n);
  }


  /* ------------------------------------------------------------
   * returnTypeAnnotation()
   *
   * Helper function for constructing the function annotation
   * of the returning type, return a empty string for Python 2.x
   * ------------------------------------------------------------ */
  String *returnTypeAnnotation(Node *n) {
    String *ret = 0;
    Parm *p = Getattr(n, "parms");
    String *tm;
    /* Try to guess the returning type by argout typemap,
     * however the result may not accurate. */
    while (p) {
      if ((tm = Getattr(p, "tmap:argout:match_type"))) {
	tm = SwigType_str(tm, 0);
	if (ret)
	  Printv(ret, ", ", tm, NULL);
	else
	  ret = tm;
	p = Getattr(p, "tmap:argout:next");
      } else {
	p = nextSibling(p);
      }
    }
    /* If no argout typemap, then get the returning type from
     * the function prototype. */
    if (!ret) {
      ret = Getattr(n, "type");
      if (ret)
	ret = SwigType_str(ret, 0);
    }
    bool funcanno = Equal(Getattr(n, "feature:python:annotations"), "c") ? true : false;
    return (ret && funcanno) ? NewStringf(" -> \"%s\"", ret) : NewString("");
  }

  /* ------------------------------------------------------------
   * variableAnnotation()
   *
   * Helper function for constructing a variable annotation
   * ------------------------------------------------------------ */

  String *variableAnnotation(Node *n) {
    String *type = Getattr(n, "type");
    if (type)
      type = SwigType_str(type, 0);
    bool anno = Equal(Getattr(n, "feature:python:annotations"), "c") ? true : false;
    anno = GetFlag(n, "feature:python:annotations:novar") ? false : anno;
    String *annotation = (type && anno) ? NewStringf(": \"%s\"", type) : NewString("");
    Delete(type);
    return annotation;
  }

  /* ------------------------------------------------------------
   * emitFunctionShadowHelper()
   *
   * Refactoring some common code out of functionWrapper and
   * dispatchFunction that writes the proxy code for non-member
   * functions.
   * ------------------------------------------------------------ */

  void emitFunctionShadowHelper(Node *n, File *f_dest, String *name, int kw) {
    String *parms = make_pyParmList(n, false, false, kw);
    String *callParms = make_pyParmList(n, false, true, kw);

    // Callbacks need the C function in order to extract the pointer from the swig_ptr: string
    bool fast = (fastproxy && !have_addtofunc(n)) || Getattr(n, "feature:callback");

    if (!fast || olddefs) {
      /* Make a wrapper function to insert the code into */
      Printv(f_dest, "\n", "def ", name, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);

      // When handling the last overloaded function in an overload set (and we're only called for the last one if the function is overloaded at all), we need to
      // output the docstring if any of the overloads has any documentation, not just this last one.
      if (Node *node_with_doc = find_overload_with_docstring(n))
	Printv(f_dest, tab4, docstring(node_with_doc, AUTODOC_FUNC, tab4, true), "\n", NIL);

      if (have_pythonprepend(n))
	Printv(f_dest, indent_pythoncode(pythonprepend(n), tab4, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
      if (have_pythonappend(n)) {
	Printv(f_dest, tab4 "val = ", funcCall(name, callParms), "\n", NIL);
	Printv(f_dest, indent_pythoncode(pythonappend(n), tab4, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
	Printv(f_dest, tab4 "return val\n", NIL);
      } else {
	Printv(f_dest, tab4 "return ", funcCall(name, callParms), "\n", NIL);
      }
    }

    // Below may result in a 2nd definition of the method when -olddefs is used. The Python interpreter will use the second definition as it overwrites the first.
    if (fast) {
      /* If there is no addtofunc directive then just assign from the extension module (for speed up) */
      Printv(f_dest, name, " = ", module, ".", name, "\n", NIL);
    }
  }


  /* ------------------------------------------------------------
   * check_kwargs()
   *
   * check if using kwargs is allowed for this Node
   * ------------------------------------------------------------ */

  int check_kwargs(Node *n) const {
    return (use_kw || GetFlag(n, "feature:kwargs"))
	&& !GetFlag(n, "memberset") && !GetFlag(n, "memberget");
  }



  /* ------------------------------------------------------------
   * add_method()
   * ------------------------------------------------------------ */

  void add_method(String *name, String *function, int kw, Node *n = 0, int funpack = 0, int num_required = -1, int num_arguments = -1) {
    String * meth_str = NewString("");
    if (!kw) {
      if (funpack) {
	if (num_required == 0 && num_arguments == 0) {
	  Printf(meth_str, "\t { \"%s\", %s, METH_NOARGS, ", name, function);
	} else if (num_required == 1 && num_arguments == 1) {
	  Printf(meth_str, "\t { \"%s\", %s, METH_O, ", name, function);
	} else {
	  Printf(meth_str, "\t { \"%s\", %s, METH_VARARGS, ", name, function);
	}
      } else {
	Printf(meth_str, "\t { \"%s\", %s, METH_VARARGS, ", name, function);
      }
    } else {
      // Cast via void(*)(void) to suppress GCC -Wcast-function-type warning.
      // Python should always call the function correctly, but the Python C API
      // requires us to store it in function pointer of a different type.
      Printf(meth_str, "\t { \"%s\", (PyCFunction)(void(*)(void))%s, METH_VARARGS|METH_KEYWORDS, ", name, function);
    }
    Append(methods, meth_str);
    if (fastproxy) {
      Append(methods_proxydocs, meth_str);
    }
    Delete(meth_str);

    if (!n) {
      Append(methods, "NULL");
      if (fastproxy) {
	Append(methods_proxydocs, "NULL");
      }
    } else if (Node *node_with_doc = find_overload_with_docstring(n)) {
      /* Use the low-level docstring here since this is the docstring that will be used for the C API */
      String *ds = cdocstring(node_with_doc, Getattr(n, "memberfunction") ? AUTODOC_METHOD : AUTODOC_FUNC, true);
      Printf(methods, "\"%s\"", ds);
      if (fastproxy) {
	/* In the fastproxy case, we must also record the high-level docstring for use in the Python shadow API */
	Delete(ds);
        ds = cdocstring(node_with_doc, Getattr(n, "memberfunction") ? AUTODOC_METHOD : AUTODOC_FUNC);
	Printf(methods_proxydocs, "\"%s\"", ds);
      }
      Delete(ds);
    } else if (Getattr(n, "feature:callback")) {
      Printf(methods, "\"swig_ptr: %s\"", Getattr(n, "feature:callback:name"));
      if (fastproxy) {
	Printf(methods_proxydocs, "\"swig_ptr: %s\"", Getattr(n, "feature:callback:name"));
	have_fast_proxy_static_member_method_callback = true;
      }
    } else {
      Append(methods, "NULL");
      if (fastproxy) {
	Append(methods_proxydocs, "NULL");
      }
    }

    Append(methods, "},\n");
    if (fastproxy) {
      Append(methods_proxydocs, "},\n");
    }
  }

  /* ------------------------------------------------------------
   * dispatchFunction()
   * ------------------------------------------------------------ */
  void dispatchFunction(Node *n, String *linkage, int funpack = 0, bool builtin_self = false, bool builtin_ctor = false, bool director_class = false, bool use_static_method = false) {
    /* Last node in overloaded chain */

    bool add_self = builtin_self && (!builtin_ctor || director_class);

    int maxargs;
    bool check_emitted = false;

    String *tmp = NewString("");
    String *dispatch;

    const char *dispatch_call = funpack ? "%s(self, argc, argv);" : (builtin_ctor ? "%s(self, args, NULL);" : "%s(self, args);");
    String *dispatch_code = NewStringf("return %s", dispatch_call);

    if (castmode) {
      dispatch = Swig_overload_dispatch_cast(n, dispatch_code, &maxargs, &check_emitted);
    } else {
      String *fastdispatch_code;
      if (builtin_ctor)
	fastdispatch_code = NewStringf("int retval = %s\nif (retval == 0 || !SWIG_Python_TypeErrorOccurred(NULL)) return retval;\nSWIG_fail;", dispatch_call);
      else
	fastdispatch_code = NewStringf("PyObject *retobj = %s\nif (!SWIG_Python_TypeErrorOccurred(retobj)) return retobj;\nSWIG_fail;", dispatch_call);
      if (!CPlusPlus) {
	Insert(fastdispatch_code, 0, "{\n");
	Append(fastdispatch_code, "\n}");
      }
      dispatch = Swig_overload_dispatch(n, dispatch_code, &maxargs, &check_emitted, fastdispatch_code);
      Delete(fastdispatch_code);
    }

    /* Generate a dispatch wrapper for all overloaded functions */

    Wrapper *f = NewWrapper();
    String *symname = Getattr(n, "sym:name");
    String *wname = Swig_name_wrapper(symname);

    const char *builtin_kwargs = builtin_ctor ? ", PyObject *kwargs" : "";
    Printv(f->def, linkage, builtin_ctor ? "int " : "PyObject *", wname, "(PyObject *self, PyObject *args", builtin_kwargs, ") {", NIL);

    if (builtin) {
      /* Avoid warning if the self parameter is not used. */
      Append(f->code, "(void)self;\n");
    }

    Wrapper_add_local(f, "argc", "Py_ssize_t argc");
    Printf(tmp, "PyObject *argv[%d] = {0}", maxargs + 1);
    Wrapper_add_local(f, "argv", tmp);

    if (!fastunpack) {
      Wrapper_add_local(f, "ii", "Py_ssize_t ii");

      if (builtin_ctor)
	Printf(f->code, "if (!SWIG_Python_CheckNoKeywords(kwargs, \"%s\")) SWIG_fail;\n", symname);

      if (maxargs - (add_self ? 1 : 0) > 0) {
        Append(f->code, "if (!PyTuple_Check(args)) SWIG_fail;\n");
        Append(f->code, "argc = PyObject_Length(args);\n");
      } else {
        Append(f->code, "argc = args ? PyObject_Length(args) : 0;\n");
      }

      if (add_self)
	Append(f->code, "argv[0] = self;\n");
      Printf(f->code, "for (ii = 0; (ii < %d) && (ii < argc); ii++) {\n", add_self ? maxargs - 1 : maxargs);
      Printf(f->code, "argv[ii%s] = PyTuple_GET_ITEM(args,ii);\n", add_self ? " + 1" : "");
      Append(f->code, "}\n");
      if (add_self)
	Append(f->code, "argc++;\n");
    } else {
      if (builtin_ctor)
	Printf(f->code, "if (!SWIG_Python_CheckNoKeywords(kwargs, \"%s\")) SWIG_fail;\n", symname);
      Printf(f->code, "if (!(argc = SWIG_Python_UnpackTuple(args, \"%s\", 0, %d, argv%s))) SWIG_fail;\n", symname, maxargs, add_self ? "+1" : "");
      if (add_self)
	Append(f->code, "argv[0] = self;\n");
      else
	Append(f->code, "--argc;\n");
    }

    Replaceall(dispatch, "$args", "self, args");

    Printv(f->code, dispatch, "\n", NIL);

    if (GetFlag(n, "feature:python:maybecall")) {
      Append(f->code, "fail:\n");
      Append(f->code, "  SWIG_Py_INCREF(Py_NotImplemented);\n");
      Append(f->code, "  return Py_NotImplemented;\n");
    } else {
      Node *sibl = n;
      while (Getattr(sibl, "sym:previousSibling"))
	sibl = Getattr(sibl, "sym:previousSibling");	// go all the way up
      String *protoTypes = NewString("");
      do {
	String *fulldecl = Swig_name_decl(sibl);
	Printf(protoTypes, "\n\"    %s\\n\"", fulldecl);
	Delete(fulldecl);
      } while ((sibl = Getattr(sibl, "sym:nextSibling")));
      Append(f->code, "fail:\n");
      Printf(f->code, "  SWIG_Python_RaiseOrModifyTypeError("
	     "\"Wrong number or type of arguments for overloaded function '%s'.\\n\"" "\n\"  Possible C/C++ prototypes are:\\n\"%s);\n", symname, protoTypes);
      Printf(f->code, "return %s;\n", builtin_ctor ? "-1" : "0");
      Delete(protoTypes);
    }
    Printv(f->code, "}\n", NIL);
    Wrapper_print(f, f_wrappers);
    if (!builtin_self && (use_static_method || !builtin))
      add_method(symname, wname, 0, Getattr(n, "sym:previousSibling") ? n : NULL);

    /* Create a shadow for this function (if enabled and not in a member function) */
    if (!builtin && shadow && !(shadow & PYSHADOW_MEMBER) && use_static_method) {
      emitFunctionShadowHelper(n, in_class ? f_shadow_stubs : f_shadow, symname, 0);
    }
    DelWrapper(f);
    Delete(dispatch);
    Delete(dispatch_code);
    Delete(tmp);
    Delete(wname);
  }

  /* ------------------------------------------------------------
   * functionWrapper()
   * ------------------------------------------------------------ */

  /*
    A note about argument marshalling with built-in types.
    There are three distinct cases for member (non-static) methods:

    1) An ordinary member function.  In this case, the first param in
    the param list is 'this'.  For builtin types, 'this' is taken from
    the first argument to the wrapper (usually called 'self); it's not
    extracted from the second argument (which is usually a tuple).

    2) A constructor for a non-director class.  In this case, the
    param list doesn't contain an entry for 'this', but the first ('self')
    argument to the wrapper *does* contain the newly-allocated,
    uninitialized object.

    3) A constructor for a director class.  In this case, the param
    list contains a 'self' param, which comes from the first argument
    to the wrapper function.
  */

  const char *get_implicitconv_flag(Node *klass) {
    int conv = 0;
    if (klass && GetFlag(klass, "feature:implicitconv")) {
      conv = 1;
    }
    return conv ? "SWIG_POINTER_IMPLICIT_CONV" : "0";
  }


  virtual int functionWrapper(Node *n) {

    String *name = Getattr(n, "name");
    String *iname = Getattr(n, "sym:name");
    SwigType *returntype = Getattr(n, "type");
    ParmList *l = Getattr(n, "parms");
    Node *parent = Swig_methodclass(n);

    int director_method = 0;

    Parm *p;
    int i;
    char source[64];
    Wrapper *f;
    String *self_parse;
    String *parse_args;
    String *arglist;
    String *get_pointers;
    String *cleanup;
    String *outarg;
    String *kwargs;
    String *tm;
    String *overname = 0;

    int num_required;
    int num_arguments;
    int num_fixed_arguments;
    int tuple_required;
    int tuple_arguments;
    int varargs = 0;
    int allow_kwargs = check_kwargs(n);

    String *nodeType = Getattr(n, "nodeType");
    int constructor = (!Cmp(nodeType, "constructor"));
    int destructor = (!Cmp(nodeType, "destructor"));
    String *storage = Getattr(n, "storage");
    int isfriend = Strstr(storage, "friend") != NULL;
    /* Only the first constructor is handled as init method. Others
       constructor can be emitted via %rename */
    int handled_as_init = 0;
    if (!have_constructor && (constructor || Getattr(n, "handled_as_constructor"))
	&& ((shadow & PYSHADOW_MEMBER))) {
      String *nname = Getattr(n, "sym:name");
      String *sname = Getattr(getCurrentClass(), "sym:name");
      String *cname = Swig_name_construct(NSPACE_TODO, sname);
      handled_as_init = (Strcmp(nname, sname) == 0) || (Strcmp(nname, cname) == 0);
      Delete(cname);
    }
    bool builtin_self = builtin && in_class && (constructor || (l && Getattr(l, "self")));
    bool builtin_ctor = false;
    if (builtin_self && constructor) {
      String *class_mname = Getattr(getCurrentClass(), "sym:name");
      String *mrename = Swig_name_construct(getNSpace(), class_mname);
      if (Cmp(iname, mrename))
	builtin_self = false;
      else
	builtin_ctor = true;
      Delete(mrename);
    }
    bool director_class = (getCurrentClass() && Swig_directorclass(getCurrentClass()));
    bool add_self = builtin_self && (!builtin_ctor || director_class);
    bool builtin_getter = (builtin && GetFlag(n, "memberget"));
    bool builtin_setter = (builtin && GetFlag(n, "memberset") && !builtin_getter);
    char const *wrap_return = builtin_ctor ? "int " : "PyObject *";
    String *linkage = NewString("SWIGINTERN ");
    String *wrapper_name = Swig_name_wrapper(iname);

    if (Getattr(n, "sym:overloaded")) {
      overname = Getattr(n, "sym:overname");
    } else {
      if (!addSymbol(iname, n))
	return SWIG_ERROR;
    }

    f = NewWrapper();
    self_parse = NewString("");
    parse_args = NewString("");
    arglist = NewString("");
    get_pointers = NewString("");
    cleanup = NewString("");
    outarg = NewString("");
    kwargs = NewString("");

    int allow_thread = threads_enable(n);

    Wrapper_add_local(f, "resultobj", "PyObject *resultobj = 0");

    // Emit all of the local variables for holding arguments.
    emit_parameter_variables(l, f);

    /* Attach the standard typemaps */
    emit_attach_parmmaps(l, f);
    Setattr(n, "wrap:parms", l);
    /* Get number of required and total arguments */
    tuple_arguments = num_arguments = emit_num_arguments(l);
    tuple_required = num_required = emit_num_required(l);
    if (add_self) {
      --tuple_arguments;
      --tuple_required;
    }
    num_fixed_arguments = tuple_required;

    // builtin handles/checks kwargs by default except in constructor wrappers so we need to explicitly handle them in the C constructor wrapper
    // The check below is for zero arguments. Sometimes (eg directors) self is the first argument for a method with zero arguments.
    if (((num_arguments == 0) && (num_required == 0)) || ((num_arguments == 1) && (num_required == 1) && Getattr(l, "self")))
      if (!builtin_ctor)
	allow_kwargs = 0;
    varargs = emit_isvarargs(l);

    String *wname = Copy(wrapper_name);
    if (overname) {
      Append(wname, overname);
    }

    const char *builtin_kwargs = builtin_ctor ? ", PyObject *kwargs" : "";
    if (!allow_kwargs || overname) {
      if (!varargs) {
	Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, PyObject *args", builtin_kwargs, ") {", NIL);
      } else {
	Printv(f->def, linkage, wrap_return, wname, "__varargs__", "(PyObject *self, PyObject *args, PyObject *varargs", builtin_kwargs, ") {", NIL);
      }
      if (allow_kwargs) {
	Swig_warning(WARN_LANG_OVERLOAD_KEYWORD, input_file, line_number, "Can't use keyword arguments with overloaded functions (%s).\n", Swig_name_decl(n));
	allow_kwargs = 0;
      }
    } else {
      if (varargs) {
	Swig_warning(WARN_LANG_VARARGS_KEYWORD, input_file, line_number, "Can't wrap varargs with keyword arguments enabled\n");
	varargs = 0;
      }
      Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, PyObject *args, PyObject *kwargs) {", NIL);
    }

    if (!builtin) {
      /* Avoid warning if the self parameter is not used. */
      Append(f->code, "(void)self;\n");
    }

    if (!builtin || !in_class || tuple_arguments > 0 || builtin_ctor) {
      if (!allow_kwargs) {
	Append(parse_args, "    if (!PyArg_ParseTuple(args, \"");
      } else {
	Append(parse_args, "    if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"");
	Append(arglist, ", kwnames");
      }
    }

    bool over_varargs = emit_isvarargs_function(n);

    int funpack = fastunpack && !varargs && !over_varargs && !allow_kwargs;
    int noargs = funpack && (tuple_required == 0 && tuple_arguments == 0);
    int onearg = funpack && (tuple_required == 1 && tuple_arguments == 1);

    if (builtin && funpack && !overname && !builtin_ctor) {
      if (!(tuple_arguments > tuple_required || varargs)) {
	String *argattr = NewStringf("%d", tuple_arguments);
	Setattr(n, "python:argcount", argattr);
	Delete(argattr);
      }
    }

    /* Generate code for argument marshalling */
    bool swig_obj_added = false;

    if (constructor && num_arguments == 1 && num_required == 1) {
      if (Cmp(storage, "explicit") == 0) {
	if (GetFlag(parent, "feature:implicitconv")) {
	  String *desc = NewStringf("SWIGTYPE%s", SwigType_manglestr(Getattr(n, "type")));
	  Printf(f->code, "if (SWIG_CheckImplicit(%s)) SWIG_fail;\n", desc);
	  Delete(desc);
	}
      }
    }

    if (builtin_ctor && checkAttribute(n, "access", "protected")) {
      String *tmp_none_comparison = Copy(none_comparison);
      Replaceall(tmp_none_comparison, "$arg", "self");
      Printf(self_parse, "if (!(%s)) {\n", tmp_none_comparison);
      Printv(self_parse, "  SWIG_SetErrorMsg(PyExc_RuntimeError, \"accessing abstract class or protected constructor\");\n  SWIG_fail;\n}\n", NIL);
      Delete(tmp_none_comparison);
    }

    int use_parse = 0;
    Append(kwargs, "{");
    for (i = 0, p = l; i < num_arguments; i++) {
      while (checkAttribute(p, "tmap:in:numinputs", "0")) {
	p = Getattr(p, "tmap:in:next");
      }

      SwigType *pt = Getattr(p, "type");
      String *ln = Getattr(p, "lname");
      bool parse_from_tuple = (i > 0 || !add_self);
      if (SwigType_type(pt) == T_VARARGS) {
	parse_from_tuple = false;
	num_fixed_arguments -= atoi(Char(Getattr(p, "tmap:in:numinputs")));
      }
      if (!parse_from_tuple)
	sprintf(source, "self");
      else if (funpack) {
	if (!swig_obj_added && !overname) {
	  sprintf(source, "PyObject *swig_obj[%d]", num_arguments);
	  Wrapper_add_localv(f, "swig_obj", source, NIL);
	  swig_obj_added = true;
	}
	sprintf(source, "swig_obj[%d]", add_self && !overname ? i - 1 : i);
      } else
	sprintf(source, "obj%d", builtin_ctor ? i + 1 : i);

      if (parse_from_tuple) {
	Printf(arglist, ", ");
	if (i == num_required)
	  Putc('|', parse_args);	/* Optional argument separator */
      }

      /* Keyword argument handling */
      if (allow_kwargs && parse_from_tuple) {
	String *name = makeParameterName(n, p, i + 1);
	Printf(kwargs, " (char *)\"%s\", ", name);
	Delete(name);
      }

      /* Look for an input typemap */
      if ((tm = Getattr(p, "tmap:in"))) {
	String *parse = Getattr(p, "tmap:in:parse");
	if (!parse) {
	  if (builtin_self) {
	    Replaceall(tm, "$self", "self");
	  } else if (funpack) {
	    Replaceall(tm, "$self", "swig_obj[0]");
	  } else {
	    Replaceall(tm, "$self", "obj0");
	  }
	  Replaceall(tm, "$input", source);
	  Setattr(p, "emit:input", source);	/* Save the location of the object */

	  if (Getattr(p, "wrap:disown") || (Getattr(p, "tmap:in:disown"))) {
	    Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
	  } else {
	    Replaceall(tm, "$disown", "0");
	  }

	  if (Getattr(p, "tmap:in:implicitconv")) {
	    const char *convflag = "0";
	    if (!Getattr(p, "hidden")) {
	      SwigType *ptype = Getattr(p, "type");
	      convflag = get_implicitconv_flag(classLookup(ptype));
	    }
	    Replaceall(tm, "$implicitconv", convflag);
	    Setattr(p, "implicitconv", convflag);
	  }

	  if (parse_from_tuple)
	    Putc('O', parse_args);
	  if (!funpack && parse_from_tuple) {
	    Wrapper_add_localv(f, source, "PyObject *", source, "= 0", NIL);
	    Printf(arglist, "&%s", source);
	  }
	  if (i >= num_required)
	    Printv(get_pointers, "if (", source, ") {\n", NIL);
	  Printv(get_pointers, tm, "\n", NIL);
	  if (i >= num_required)
	    Printv(get_pointers, "}\n", NIL);

	} else {
	  use_parse = 1;
	  Append(parse_args, parse);
	  if (parse_from_tuple)
	    Printf(arglist, "&%s", ln);
	}
	p = Getattr(p, "tmap:in:next");
	continue;
      } else {
	Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
	break;
      }
    }

    /* finish argument marshalling */
    Append(kwargs, " NULL }");
    if (allow_kwargs) {
      Printv(f->locals, "  char * kwnames[] = ", kwargs, ";\n", NIL);
    }

    if (use_parse || allow_kwargs) {
      Printf(parse_args, ":%s\"", iname);
      Printv(parse_args, arglist, ")) SWIG_fail;\n", NIL);
      funpack = 0;
    } else {
      Clear(parse_args);

      if (funpack) {
	Clear(f->def);
	if (overname) {
	  if (noargs) {
	    Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, Py_ssize_t nobjs, PyObject **SWIGUNUSEDPARM(swig_obj)) {", NIL);
	  } else {
	    Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {", NIL);
	  }
	  Printf(parse_args, "if ((nobjs < %d) || (nobjs > %d)) SWIG_fail;\n", num_required, num_arguments);
	} else {
	  int is_tp_call = Equal(Getattr(n, "feature:python:slot"), "tp_call");
	  Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, PyObject *args", builtin_kwargs, ") {", NIL);
	  if (builtin_ctor)
	    Printf(parse_args, "if (!SWIG_Python_CheckNoKeywords(kwargs, \"%s\")) SWIG_fail;\n", iname);
	  if (onearg && !builtin_ctor && !is_tp_call) {
	    Printf(parse_args, "if (!args) SWIG_fail;\n");
	    Append(parse_args, "swig_obj[0] = args;\n");
	  } else if (!noargs) {
	    Printf(parse_args, "if (!SWIG_Python_UnpackTuple(args, \"%s\", %d, %d, swig_obj)) SWIG_fail;\n", iname, num_fixed_arguments, tuple_arguments);
	  } else if (noargs) {
	    Printf(parse_args, "if (!SWIG_Python_UnpackTuple(args, \"%s\", %d, %d, 0)) SWIG_fail;\n", iname, num_fixed_arguments, tuple_arguments);
	  }
	}
      } else {
	if (builtin_ctor)
	  Printf(parse_args, "if (!SWIG_Python_CheckNoKeywords(kwargs, \"%s\")) SWIG_fail;\n", iname);
	if (builtin && in_class && tuple_arguments == 0) {
	  Printf(parse_args, "    if (args && PyTuple_Check(args) && PyTuple_GET_SIZE(args) > 0) SWIG_exception_fail(SWIG_TypeError, \"%s takes no arguments\");\n", iname);
	} else {
	  Printf(parse_args, "if (!PyArg_UnpackTuple(args, \"%s\", %d, %d", iname, num_fixed_arguments, tuple_arguments);
	  Printv(parse_args, arglist, ")) SWIG_fail;\n", NIL);
	}
      }
    }

    /* Now piece together the first part of the wrapper function */
    Printv(f->code, self_parse, parse_args, get_pointers, NIL);

    /* Check for trailing varargs */
    if (varargs) {
      if (p && (tm = Getattr(p, "tmap:in"))) {
	Replaceall(tm, "$input", "varargs");
	Printv(f->code, tm, "\n", NIL);
      }
    }

    /* Insert constraint checking code */
    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:check"))) {
	Printv(f->code, tm, "\n", NIL);
	p = Getattr(p, "tmap:check:next");
      } else {
	p = nextSibling(p);
      }
    }

    /* Insert cleanup code */
    for (p = l; p;) {
      if (!Getattr(p, "tmap:in:parse") && (tm = Getattr(p, "tmap:freearg"))) {
	if (Getattr(p, "tmap:freearg:implicitconv")) {
	  const char *convflag = "0";
	  if (!Getattr(p, "hidden")) {
	    SwigType *ptype = Getattr(p, "type");
	    convflag = get_implicitconv_flag(classLookup(ptype));
	  }
	  if (strcmp(convflag, "0") == 0) {
	    tm = 0;
	  }
	}
	if (tm && (Len(tm) != 0)) {
	  Printv(cleanup, tm, "\n", NIL);
	}
	p = Getattr(p, "tmap:freearg:next");
      } else {
	p = nextSibling(p);
      }
    }

    /* Insert argument output code */
    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:argout"))) {
	Replaceall(tm, "$arg", Getattr(p, "emit:input"));
	Replaceall(tm, "$input", Getattr(p, "emit:input"));
	Printv(outarg, tm, "\n", NIL);
	p = Getattr(p, "tmap:argout:next");
      } else {
	p = nextSibling(p);
      }
    }

    /* if the object is a director, and the method call originated from its
     * underlying python object, resolve the call by going up the c++
     * inheritance chain.  otherwise try to resolve the method in python.
     * without this check an infinite loop is set up between the director and
     * shadow class method calls.
     */

    // NOTE: this code should only be inserted if this class is the
    // base class of a director class.  however, in general we haven't
    // yet analyzed all classes derived from this one to see if they are
    // directors.  furthermore, this class may be used as the base of
    // a director class defined in a completely different module at a
    // later time, so this test must be included whether or not directorbase
    // is true.  we do skip this code if directors have not been enabled
    // at the command line to preserve source-level compatibility with
    // non-polymorphic swig.  also, if this wrapper is for a smart-pointer
    // method, there is no need to perform the test since the calling object
    // (the smart-pointer) and the director object (the "pointee") are
    // distinct.

    director_method = is_member_director(n) && !is_smart_pointer() && !destructor;
    if (director_method) {
      Wrapper_add_local(f, "director", "Swig::Director *director = 0");
      Append(f->code, "director = SWIG_DIRECTOR_CAST(arg1);\n");
      if (dirprot_mode() && !is_public(n)) {
	Printf(f->code, "if (!director || !(director->swig_get_inner(\"%s\"))) {\n", name);
	Printf(f->code, "SWIG_SetErrorMsg(PyExc_RuntimeError,\"accessing protected member %s\");\n", name);
	Append(f->code, "SWIG_fail;\n");
	Append(f->code, "}\n");
      }
      Wrapper_add_local(f, "upcall", "bool upcall = false");
      if (funpack) {
	const char *self_parm = builtin_self ? "self" : "swig_obj[0]";
	Printf(f->code, "upcall = (director && (director->swig_get_self()==%s));\n", self_parm);
      } else {
	const char *self_parm = builtin_self ? "self" : "obj0";
	Printf(f->code, "upcall = (director && (director->swig_get_self()==%s));\n", self_parm);
      }
    }

    /* Emit the function call */
    if (director_method) {
      Append(f->code, "try {\n");
    } else {
      if (allow_thread) {
	String *preaction = NewString("");
	thread_begin_allow(n, preaction);
	Setattr(n, "wrap:preaction", preaction);

	String *postaction = NewString("");
	thread_end_allow(n, postaction);
	Setattr(n, "wrap:postaction", postaction);
      }
    }

    Setattr(n, "wrap:name", wname);

    Swig_director_emit_dynamic_cast(n, f);
    String *actioncode = emit_action(n);

    if (director_method) {
      Append(actioncode, "} catch (Swig::DirectorException&) {\n");
      Append(actioncode, "  SWIG_fail;\n");
      Append(actioncode, "}\n");
    }

    /* This part below still needs cleanup */

    /* Return the function value */
    tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode);

    if (tm) {
      if (builtin_self) {
	Replaceall(tm, "$self", "self");
      } else if (funpack) {
	Replaceall(tm, "$self", "swig_obj[0]");
      } else {
	Replaceall(tm, "$self", "obj0");
      }
      Replaceall(tm, "$result", "resultobj");
      if (builtin_ctor) {
	Replaceall(tm, "$owner", "SWIG_BUILTIN_INIT");
      } else if (handled_as_init) {
	Replaceall(tm, "$owner", "SWIG_POINTER_NEW");
      } else {
	if (GetFlag(n, "feature:new")) {
	  Replaceall(tm, "$owner", "SWIG_POINTER_OWN");
	} else {
	  Replaceall(tm, "$owner", "0");
	}
      }

      // Unwrap return values that are director classes so that the original Python object is returned instead. 
      if (!constructor && Swig_director_can_unwrap(n)) {
	Wrapper_add_local(f, "director", "Swig::Director *director = 0");
	Printf(f->code, "director = SWIG_DIRECTOR_CAST(%s);\n", Swig_cresult_name());
	Append(f->code, "if (director) {\n");
	Append(f->code, "  resultobj = director->swig_get_self();\n");
	Append(f->code, "  SWIG_Py_INCREF(resultobj);\n");
	Append(f->code, "} else {\n");
	Printf(f->code, "%s\n", tm);
	Append(f->code, "}\n");
      } else {
	Printf(f->code, "%s\n", tm);
      }

      Delete(tm);
    } else {
      Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(returntype, 0), name);
    }
    emit_return_variable(n, returntype, f);

    /* Output argument output code */
    Printv(f->code, outarg, NIL);

    /* Output cleanup code */
    int need_cleanup = Len(cleanup) != 0;
    if (need_cleanup) {
      Printv(f->code, cleanup, NIL);
    }

    /* Look to see if there is any newfree cleanup code */
    if (GetFlag(n, "feature:new")) {
      if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
	Printf(f->code, "%s\n", tm);
	Delete(tm);
      }
    }

    /* See if there is any return cleanup code */
    if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
      Printf(f->code, "%s\n", tm);
      Delete(tm);
    }

    if (director_method) {
      if ((tm = Swig_typemap_lookup("directorfree", n, Swig_cresult_name(), 0))) {
	Replaceall(tm, "$input", Swig_cresult_name());
	Replaceall(tm, "$result", "resultobj");
	Printf(f->code, "%s\n", tm);
	Delete(tm);
      }
    }

    if (builtin_ctor)
      Append(f->code, "    return resultobj == Py_None ? -1 : 0;\n");
    else
      Append(f->code, "    return resultobj;\n");

    /* Error handling code */

    Append(f->code, "fail:\n");
    if (need_cleanup) {
      Printv(f->code, cleanup, NIL);
    }
    if (builtin_ctor) {
      Printv(f->code, "  return -1;\n", NIL);
    } else {
      if (GetFlag(n, "feature:python:maybecall")) {
	Append(f->code, "  if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)) {\n");
	Append(f->code, "    return NULL;\n");
	Append(f->code, "  }\n");
	Append(f->code, "  PyErr_Clear();\n");
	Append(f->code, "  SWIG_Py_INCREF(Py_NotImplemented);\n");
	Append(f->code, "  return Py_NotImplemented;\n");
      } else {
        Printv(f->code, "  return NULL;\n", NIL);
      }
    }

    Append(f->code, "}\n");

    /* Substitute the cleanup code */
    Replaceall(f->code, "$cleanup", cleanup);

    bool isvoid = !Cmp(returntype, "void");
    Replaceall(f->code, "$isvoid", isvoid ? "1" : "0");

    /* Substitute the function name */
    Replaceall(f->code, "$symname", iname);
    Replaceall(f->code, "$result", "resultobj");

    if (builtin_self) {
      Replaceall(f->code, "$self", "self");
    } else if (funpack) {
      Replaceall(f->code, "$self", "swig_obj[0]");
    } else {
      Replaceall(f->code, "$self", "obj0");
    }

    /* Dump the function out */
    Wrapper_print(f, f_wrappers);

    /* If varargs.  Need to emit a varargs stub */
    if (varargs) {
      DelWrapper(f);
      f = NewWrapper();
      if (funpack) {
	// Note: funpack is currently always false for varargs
	Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) {", NIL);
      } else {
	Printv(f->def, linkage, wrap_return, wname, "(PyObject *self, PyObject *args", builtin_kwargs, ") {", NIL);
      }
      Wrapper_add_local(f, "resultobj", builtin_ctor ? "int resultobj" : "PyObject *resultobj");
      Wrapper_add_local(f, "varargs", "PyObject *varargs");
      Wrapper_add_local(f, "newargs", "PyObject *newargs");
      if (funpack) {
	Wrapper_add_local(f, "i", "int i");
	Printf(f->code, "newargs = PyTuple_New(%d);\n", num_fixed_arguments);
	Printf(f->code, "for (i = 0; i < %d; ++i) {\n", num_fixed_arguments);
	Printf(f->code, "  PyTuple_SET_ITEM(newargs, i, swig_obj[i]);\n");
	Printf(f->code, "  SWIG_Py_XINCREF(swig_obj[i]);\n");
	Printf(f->code, "}\n");
	Printf(f->code, "varargs = PyTuple_New(nobjs > %d ? nobjs - %d : 0);\n", num_fixed_arguments, num_fixed_arguments);
	Printf(f->code, "for (i = 0; i < nobjs - %d; ++i) {\n", num_fixed_arguments);
	Printf(f->code, "  PyTuple_SET_ITEM(newargs, i, swig_obj[i + %d]);\n", num_fixed_arguments);
	Printf(f->code, "  SWIG_Py_XINCREF(swig_obj[i + %d]);\n", num_fixed_arguments);
	Printf(f->code, "}\n");
      } else {
	Printf(f->code, "newargs = PyTuple_GetSlice(args, 0, %d);\n", num_fixed_arguments);
	Printf(f->code, "varargs = PyTuple_GetSlice(args, %d, PyTuple_Size(args));\n", num_fixed_arguments);
      }
      Printf(f->code, "resultobj = %s__varargs__(%s, newargs, varargs%s);\n", wname, builtin ? "self" : "NULL", strlen(builtin_kwargs) == 0 ? "" : ", kwargs");
      Append(f->code, "SWIG_Py_XDECREF(newargs);\n");
      Append(f->code, "SWIG_Py_XDECREF(varargs);\n");
      Append(f->code, "return resultobj;\n");
      Append(f->code, "}\n");
      Wrapper_print(f, f_wrappers);
    }

    bool use_static_method = flat_static_method || !Swig_storage_isstatic_custom(n, "staticmemberfunctionHandler:storage");
    /* Now register the function with the interpreter.   */
    if (!Getattr(n, "sym:overloaded")) {
      if (!builtin_self && (use_static_method || !builtin))
	add_method(iname, wname, allow_kwargs, n, funpack, num_required, num_arguments);

      /* Create a shadow for this function (if enabled and not in a member function) */
      if (!builtin && shadow && !(shadow & PYSHADOW_MEMBER) && use_static_method) {
	emitFunctionShadowHelper(n, in_class ? f_shadow_stubs : f_shadow, iname, allow_kwargs);
      }

    } else {
      if (!Getattr(n, "sym:nextSibling")) {
	dispatchFunction(n, linkage, funpack, builtin_self, builtin_ctor, director_class, use_static_method);
      }
    }

    // Put this in tp_init of the PyTypeObject
    if (builtin_ctor) {
      if ((director_method || !is_private(n)) && !Getattr(class_members, iname)) {
	Setattr(class_members, iname, n);
	if (!builtin_tp_init)
	  builtin_tp_init = Swig_name_wrapper(iname);
      }
    }

    /* If this is a builtin type, create a PyGetSetDef entry for this member variable. */
    if (builtin) {
      const char *memname = "__dict__";
      Hash *h = Getattr(builtin_getset, memname);
      if (!h) {
        h = NewHash();
        Setattr(builtin_getset, memname, h);
        Delete(h);
      }
      Setattr(h, "getter", "SwigPyObject_get___dict__");
      if (!Getattr(h, "doc")) {
	Setattr(h, "doc", "dictionary for instance variables");
      }
    }

    if (builtin_getter) {
      String *memname = Getattr(n, "membervariableHandler:sym:name");
      if (!memname)
	memname = iname;
      Hash *h = Getattr(builtin_getset, memname);
      if (!h) {
	h = NewHash();
	Setattr(builtin_getset, memname, h);
	Delete(h);
      }
      Setattr(h, "getter", wrapper_name);
      Delattr(n, "memberget");
      if (!Getattr(h, "doc")) {
	Setattr(n, "doc:high:name", Getattr(n, "name"));
	String *ds = cdocstring(n, AUTODOC_VAR);
	Setattr(h, "doc", ds);
	Delete(ds);
      }
    }
    if (builtin_setter) {
      String *memname = Getattr(n, "membervariableHandler:sym:name");
      if (!memname)
	memname = iname;
      Hash *h = Getattr(builtin_getset, memname);
      if (!h) {
	h = NewHash();
	Setattr(builtin_getset, memname, h);
	Delete(h);
      }
      Setattr(h, "setter", wrapper_name);
      Delattr(n, "memberset");
      if (!Getattr(h, "doc")) {
	Setattr(n, "doc:high:name", Getattr(n, "name"));
	String *ds = cdocstring(n, AUTODOC_VAR);
	Setattr(h, "doc", ds);
	Delete(ds);
      }
    }

    if (in_class && builtin) {
      /* Handle operator overloads for builtin types */
      String *slot = Getattr(n, "feature:python:slot");
      if (slot && !isfriend) {
	String *func_type = Getattr(n, "feature:python:slot:functype");
	String *closure_decl = getClosure(func_type, wrapper_name, overname ? 0 : funpack);
	String *feature_name = NewStringf("feature:python:%s", slot);
	String *closure_name = 0;
	if (closure_decl) {
	  closure_name = NewStringf("%s_%s_closure", wrapper_name, func_type);
	  if (!GetFlag(builtin_closures, closure_name))
	    Printf(builtin_closures_code, "%s /* defines %s */\n\n", closure_decl, closure_name);
	  SetFlag(builtin_closures, closure_name);
	  Delete(closure_decl);
	} else {
	  closure_name = Copy(wrapper_name);
	}
	if (func_type) {
	  String *s = NewStringf("%s", closure_name);
	  Delete(closure_name);
	  closure_name = s;
	}
	Setattr(parent, feature_name, closure_name);
	Delete(feature_name);
	Delete(closure_name);
      }

      /* Handle comparison operators for builtin types */
      String *compare = Getattr(n, "feature:python:compare");
      if (compare) {
	Hash *richcompare = Getattr(parent, "python:richcompare");
	assert(richcompare);
	Setattr(richcompare, compare, wrapper_name);
      }
    }

    Delete(self_parse);
    Delete(parse_args);
    Delete(linkage);
    Delete(arglist);
    Delete(get_pointers);
    Delete(cleanup);
    Delete(outarg);
    Delete(kwargs);
    Delete(wname);
    DelWrapper(f);
    Delete(wrapper_name);
    return SWIG_OK;
  }



  /* ------------------------------------------------------------
   * variableWrapper()
   * ------------------------------------------------------------ */

  virtual int variableWrapper(Node *n) {
    String *name = Getattr(n, "name");
    String *iname = Getattr(n, "sym:name");
    SwigType *t = Getattr(n, "type");

    static int have_globals = 0;
    String *tm;
    Wrapper *getf, *setf;

    if (!addSymbol(iname, n))
      return SWIG_ERROR;

    getf = NewWrapper();
    setf = NewWrapper();

    /* If this is our first call, add the globals variable to the
       Python dictionary. */

    if (!have_globals) {
      Printf(f_init, "\t globals = SWIG_globals();\n");
      Printf(f_init, "\t if (!globals) {\n");
      Printf(f_init, "     PyErr_SetString(PyExc_TypeError, \"Failure to create SWIG globals.\");\n");
      Printf(f_init, "\t   return -1;\n");
      Printf(f_init, "\t }\n");
      Printf(f_init, "\t PyDict_SetItemString(md, \"%s\", globals);\n", global_name);
      if (builtin)
	Printf(f_init, "\t SwigPyBuiltin_AddPublicSymbol(public_interface, \"%s\");\n", global_name);
      have_globals = 1;
      if (!builtin && shadow && !(shadow & PYSHADOW_MEMBER)) {
	Printf(f_shadow_stubs, "%s = %s.%s\n", global_name, module, global_name);
      }
    }
    int assignable = !is_immutable(n);

    if (!builtin && shadow && !assignable && !in_class)
      Printf(f_shadow_stubs, "%s = %s.%s\n", iname, global_name, iname);

    String *getname = Swig_name_get(NSPACE_TODO, iname);
    String *setname = Swig_name_set(NSPACE_TODO, iname);
    String *vargetname = NewStringf("Swig_var_%s", getname);
    String *varsetname = NewStringf("Swig_var_%s", setname);

    /* Create a function for setting the value of the variable */
    if (assignable) {
      Setattr(n, "wrap:name", varsetname);
      if (builtin && in_class) {
	String *set_wrapper = Swig_name_wrapper(setname);
	Setattr(n, "pybuiltin:setter", set_wrapper);
	Delete(set_wrapper);
      }
      Printf(setf->def, "SWIGINTERN int %s(PyObject *_val) {", varsetname);
      if ((tm = Swig_typemap_lookup("varin", n, name, 0))) {
	Replaceall(tm, "$input", "_val");
	if (Getattr(n, "tmap:varin:implicitconv")) {
	  Replaceall(tm, "$implicitconv", get_implicitconv_flag(n));
	}
	emit_action_code(n, setf->code, tm);
	Delete(tm);
      } else {
	Swig_warning(WARN_TYPEMAP_VARIN_UNDEF, input_file, line_number, "Unable to set variable of type %s.\n", SwigType_str(t, 0));
      }
      Printv(setf->code, "  return 0;\n", NULL);
      Append(setf->code, "fail:\n");
      Printv(setf->code, "  return 1;\n", NULL);
    } else {
      /* Is a readonly variable.  Issue an error */
      if (CPlusPlus) {
	Printf(setf->def, "SWIGINTERN int %s(PyObject *) {", varsetname);
      } else {
	Printf(setf->def, "SWIGINTERN int %s(PyObject *_val SWIGUNUSED) {", varsetname);
      }
      Printv(setf->code, "  SWIG_Error(SWIG_AttributeError,\"Variable ", iname, " is read-only.\");\n", "  return 1;\n", NIL);
    }

    Append(setf->code, "}\n");
    Wrapper_print(setf, f_wrappers);

    /* Create a function for getting the value of a variable */
    Setattr(n, "wrap:name", vargetname);
    if (builtin && in_class) {
      String *get_wrapper = Swig_name_wrapper(getname);
      Setattr(n, "pybuiltin:getter", get_wrapper);
      Delete(get_wrapper);
    }
    int addfail = 0;
    Printf(getf->def, "SWIGINTERN PyObject *%s(void) {", vargetname);
    Wrapper_add_local(getf, "pyobj", "PyObject *pyobj = 0");
    if (builtin) {
      Wrapper_add_local(getf, "self", "PyObject *self = 0");
      Append(getf->code, "  (void)self;\n");
    }
    if ((tm = Swig_typemap_lookup("varout", n, name, 0))) {
      Replaceall(tm, "$result", "pyobj");
      addfail = emit_action_code(n, getf->code, tm);
      Delete(tm);
    } else {
      Swig_warning(WARN_TYPEMAP_VAROUT_UNDEF, input_file, line_number, "Unable to read variable of type %s\n", SwigType_str(t, 0));
    }
    Append(getf->code, "  return pyobj;\n");
    if (addfail) {
      Append(getf->code, "fail:\n");
      Append(getf->code, "  return NULL;\n");
    }
    Append(getf->code, "}\n");

    Wrapper_print(getf, f_wrappers);

    /* Now add this to the variable linking mechanism */
    Printf(f_varlinks, "\t SWIG_addvarlink(globals, \"%s\", %s, %s);\n", iname, vargetname, varsetname);
    if (builtin && shadow && !assignable && !in_class) {
      Printf(f_varlinks, "\t PyDict_SetItemString(md, \"%s\", PyObject_GetAttrString(globals, \"%s\"));\n", iname, iname);
      Printf(f_varlinks, "\t SwigPyBuiltin_AddPublicSymbol(public_interface, \"%s\");\n", iname);
    }
    Delete(vargetname);
    Delete(varsetname);
    Delete(getname);
    Delete(setname);
    DelWrapper(setf);
    DelWrapper(getf);
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * constantWrapper()
   * ------------------------------------------------------------ */

  /* Determine if the node requires the _swigconstant code to be generated */
  bool needs_swigconstant(Node *n) {
    SwigType *type = Getattr(n, "type");
    SwigType *qtype = SwigType_typedef_resolve_all(type);
    SwigType *uqtype = SwigType_strip_qualifiers(qtype);
    bool result = false;

    /* Note, that we need special handling for function pointers, as
     * SwigType_base(fptr) does not return the underlying pointer-to-function
     * type but the return-type of function. */
    if (!SwigType_isfunction(uqtype) && !SwigType_isfunctionpointer(uqtype)) {
      SwigType *basetype = SwigType_base(uqtype);
      result = SwigType_isclass(basetype) != 0;
      Delete(basetype);
    }

    Delete(qtype);
    Delete(uqtype);

    return result;
  }

  virtual int constantWrapper(Node *n) {
    String *name = Getattr(n, "name");
    String *iname = Getattr(n, "sym:name");
    SwigType *type = Getattr(n, "type");
    String *value = Getattr(n, "value");
    String *tm;
    int have_tm = 0;
    int have_builtin_symname = 0;

    if (!addSymbol(iname, n))
      return SWIG_ERROR;

    /* Special hook for member pointer */
    if (SwigType_type(type) == T_MPOINTER) {
      String *wname = Swig_name_wrapper(iname);
      String *str = SwigType_str(type, wname);
      Printf(f_header, "static %s = %s;\n", str, value);
      Delete(str);
      value = wname;
    }

    if ((tm = Swig_typemap_lookup("consttab", n, name, 0))) {
      Replaceall(tm, "$value", value);
      Printf(const_code, "%s,\n", tm);
      Delete(tm);
      have_tm = 1;
    }


    if (builtin && in_class && Getattr(n, "pybuiltin:symname")) {
      have_builtin_symname = 1;
      Swig_require("builtin_constantWrapper", n, "*sym:name", "pybuiltin:symname", NIL);
      Setattr(n, "sym:name", Getattr(n, "pybuiltin:symname"));
    }

    if ((tm = Swig_typemap_lookup("constcode", n, name, 0))) {
      Replaceall(tm, "$value", value);
      if (needs_swigconstant(n) && !builtin && shadow && !(shadow & PYSHADOW_MEMBER) && (!in_class || !Getattr(n, "feature:python:callback"))) {
	// Generate `*_swigconstant()` method which registers the new constant.
	//
	// *_swigconstant methods are required for constants of class type.
	// Class types are registered in shadow file (see *_swigregister). The
	// instances of class must be created (registered) after the type is
	// registered, so we can't let SWIG_init() to register constants of
	// class type (the SWIG_init() is called before shadow classes are
	// defined and registered).
        Printf(f_wrappers, "SWIGINTERN PyObject *%s_swigconstant(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {\n", iname);
        Printf(f_wrappers, tab2 "PyObject *module = NULL;\n");
        Printf(f_wrappers, tab2 "PyObject *d;\n");
	Printf(f_wrappers, tab2 "if (!SWIG_Python_UnpackTuple(args, \"swigconstant\", 1, 1, &module)) return NULL;\n");
        Printf(f_wrappers, tab2 "d = PyModule_GetDict(module);\n");
        Printf(f_wrappers, tab2 "if (!d) return NULL;\n");
        Printf(f_wrappers, tab2 "%s\n", tm);
        Printf(f_wrappers, tab2 "return SWIG_Py_Void();\n");
        Printf(f_wrappers, "}\n\n\n");

        // Register the method in SwigMethods array
	String *cname = NewStringf("%s_swigconstant", iname);
	add_method(cname, cname, 0, 0, 1, 1, 1);
	Delete(cname);
      } else {
        Printf(f_init, "%s\n", tm);
      }
      Delete(tm);
      have_tm = 1;
    }

    if (have_builtin_symname)
      Swig_restore(n);

    if (!have_tm) {
      Swig_warning(WARN_TYPEMAP_CONST_UNDEF, input_file, line_number, "Unsupported constant value.\n");
      return SWIG_NOWRAP;
    }

    if (!builtin && shadow && !(shadow & PYSHADOW_MEMBER)) {
      String *f_s;
      if (!in_class) {
	f_s = f_shadow;
      } else {
	f_s = Getattr(n, "feature:python:callback") ? NIL : f_shadow_stubs;
      }

      if (f_s) {
	if (needs_swigconstant(n)) {
	  Printv(f_s, "\n",NIL);
	  Printv(f_s, module, ".", iname, "_swigconstant(",module,")\n", NIL);
	}
	Printv(f_s, iname, " = ", module, ".", iname, "\n", NIL);
	if (have_docstring(n))
	  Printv(f_s, docstring(n, AUTODOC_CONST, tab4), "\n", NIL);
      }
    }
    return SWIG_OK;
  }


  /* ------------------------------------------------------------
   * nativeWrapper()
   * ------------------------------------------------------------ */

  virtual int nativeWrapper(Node *n) {
    String *name = Getattr(n, "sym:name");
    String *wrapname = Getattr(n, "wrap:name");

    if (!addSymbol(wrapname, n))
      return SWIG_ERROR;

    add_method(name, wrapname, 0);
    if (!builtin && shadow) {
      Printv(f_shadow_stubs, name, " = ", module, ".", name, "\n", NIL);
    }
    return SWIG_OK;
  }



  /* ----------------------------------------------------------------------------
   * BEGIN C++ Director Class modifications
   * ------------------------------------------------------------------------- */

  /* C++/Python polymorphism demo code
   *
   * TODO
   *
   * Move some boilerplate code generation to Swig_...() functions.
   *
   */

  /* ---------------------------------------------------------------
   * classDirectorMethod()
   *
   * Emit a virtual director method to pass a method call on to the
   * underlying Python object.
   * ** Moved down due to gcc-2.96 internal error **
   * --------------------------------------------------------------- */

  int classDirectorMethods(Node *n);

  int classDirectorMethod(Node *n, Node *parent, String *super);

  /* ------------------------------------------------------------
   * classDirectorConstructor()
   * ------------------------------------------------------------ */

  int classDirectorConstructor(Node *n) {
    Node *parent = Getattr(n, "parentNode");
    String *sub = NewString("");
    String *decl = Getattr(n, "decl");
    String *supername = Swig_class_name(parent);
    String *classname = NewString("");
    Printf(classname, "SwigDirector_%s", supername);

    /* insert self parameter */
    Parm *p;
    ParmList *superparms = Getattr(n, "parms");
    ParmList *parms = CopyParmList(superparms);
    String *type = NewString("PyObject");
    SwigType_add_pointer(type);
    p = NewParm(type, NewString("self"), n);
    set_nextSibling(p, parms);
    parms = p;

    if (!Getattr(n, "defaultargs")) {
      /* constructor */
      {
	Wrapper *w = NewWrapper();
	String *call;
	String *basetype = Getattr(parent, "classtype");
	String *target = Swig_method_decl(0, decl, classname, parms, 0);
	call = Swig_csuperclass_call(0, basetype, superparms);
	Printf(w->def, "%s::%s: %s, Swig::Director(self) { \n", classname, target, call);
	Printf(w->def, "   SWIG_DIRECTOR_RGTR((%s *)this, this); \n", basetype);
	Append(w->def, "}\n");
	Delete(target);
	Wrapper_print(w, f_directors);
	Delete(call);
	DelWrapper(w);
      }

      /* constructor header */
      {
	String *target = Swig_method_decl(0, decl, classname, parms, 1);
	Printf(f_directors_h, "    %s;\n", target);
	Delete(target);
      }
    }

    Delete(sub);
    Delete(classname);
    Delete(supername);
    Delete(parms);
    return Language::classDirectorConstructor(n);
  }

  /* ------------------------------------------------------------
   * classDirectorDefaultConstructor()
   * ------------------------------------------------------------ */

  int classDirectorDefaultConstructor(Node *n) {
    String *classname = Swig_class_name(n);
    {
      Node *parent = Swig_methodclass(n);
      String *basetype = Getattr(parent, "classtype");
      Wrapper *w = NewWrapper();
      Printf(w->def, "SwigDirector_%s::SwigDirector_%s(PyObject *self) : Swig::Director(self) { \n", classname, classname);
      Printf(w->def, "   SWIG_DIRECTOR_RGTR((%s *)this, this); \n", basetype);
      Append(w->def, "}\n");
      Wrapper_print(w, f_directors);
      DelWrapper(w);
    }
    Printf(f_directors_h, "    SwigDirector_%s(PyObject *self);\n", classname);
    Delete(classname);
    return Language::classDirectorDefaultConstructor(n);
  }


  /* ------------------------------------------------------------
   * classDirectorInit()
   * ------------------------------------------------------------ */

  int classDirectorInit(Node *n) {
    String *declaration = Swig_director_declaration(n);
    Printf(f_directors_h, "\n");
    Printf(f_directors_h, "%s\n", declaration);
    Printf(f_directors_h, "public:\n");
    Delete(declaration);
    return Language::classDirectorInit(n);
  }

  /* ------------------------------------------------------------
   * classDirectorEnd()
   * ------------------------------------------------------------ */

  int classDirectorEnd(Node *n) {
    String *classname = Swig_class_name(n);

    if (dirprot_mode()) {
      /*
         This implementation uses a std::map<std::string,int>.

         It should be possible to rewrite it using a more elegant way,
         like copying the Java approach for the 'override' array.

         But for now, this seems to be the least intrusive way.
       */
      Printf(f_directors_h, "\n");
      Printf(f_directors_h, "/* Internal director utilities */\n");
      Printf(f_directors_h, "public:\n");
      Printf(f_directors_h, "    bool swig_get_inner(const char *swig_protected_method_name) const {\n");
      Printf(f_directors_h, "      std::map<std::string, bool>::const_iterator iv = swig_inner.find(swig_protected_method_name);\n");
      Printf(f_directors_h, "      return (iv != swig_inner.end() ? iv->second : false);\n");
      Printf(f_directors_h, "    }\n");

      Printf(f_directors_h, "    void swig_set_inner(const char *swig_protected_method_name, bool swig_val) const {\n");
      Printf(f_directors_h, "      swig_inner[swig_protected_method_name] = swig_val;\n");
      Printf(f_directors_h, "    }\n");
      Printf(f_directors_h, "private:\n");
      Printf(f_directors_h, "    mutable std::map<std::string, bool> swig_inner;\n");

    }
    if (director_method_index) {
      Printf(f_directors_h, "\n");
      Printf(f_directors_h, "#if defined(SWIG_PYTHON_DIRECTOR_VTABLE)\n");
      Printf(f_directors_h, "/* VTable implementation */\n");
      Printf(f_directors_h, "    PyObject *swig_get_method(size_t method_index, const char *method_name) const {\n");
      Printf(f_directors_h, "      PyObject *method = vtable[method_index];\n");
      Printf(f_directors_h, "      if (!method) {\n");
      Printf(f_directors_h, "        swig::SwigVar_PyObject name = SWIG_Python_str_FromChar(method_name);\n");
      Printf(f_directors_h, "        method = PyObject_GetAttr(swig_get_self(), name);\n");
      Printf(f_directors_h, "        if (!method) {\n");
      Printf(f_directors_h, "          std::string msg = \"Method in class %s doesn't exist, undefined \";\n", classname);
      Printf(f_directors_h, "          msg += method_name;\n");
      Printf(f_directors_h, "          Swig::DirectorMethodException::raise(msg.c_str());\n");
      Printf(f_directors_h, "        }\n");
      Printf(f_directors_h, "        vtable[method_index] = method;\n");
      Printf(f_directors_h, "      }\n");
      Printf(f_directors_h, "      return method;\n");
      Printf(f_directors_h, "    }\n");
      Printf(f_directors_h, "private:\n");
      Printf(f_directors_h, "    mutable swig::SwigVar_PyObject vtable[%d];\n", director_method_index);
      Printf(f_directors_h, "#endif\n\n");
    }

    Printf(f_directors_h, "};\n\n");
    return Language::classDirectorEnd(n);
  }


  /* ------------------------------------------------------------
   * classDirectorDisown()
   * ------------------------------------------------------------ */

  int classDirectorDisown(Node *n) {
    int result;
    int oldshadow = shadow;
    /* disable shadowing */
    if (shadow)
      shadow = shadow | PYSHADOW_MEMBER;
    result = Language::classDirectorDisown(n);
    shadow = oldshadow;
    if (shadow) {
      if (builtin) {
	String *rname = SwigType_namestr(real_classname);
	Printf(builtin_methods, "  { \"__disown__\", Swig::Director::swig_pyobj_disown< %s >, METH_NOARGS, \"\" },\n", rname);
	Delete(rname);
      } else {
	String *symname = Getattr(n, "sym:name");
	String *mrename = Swig_name_disown(NSPACE_TODO, symname);	//Getattr(n, "name"));
	Printv(f_shadow, tab4, "def __disown__(self):\n", NIL);
	Printv(f_shadow, tab8, "self.this.disown()\n", NIL);
	Printv(f_shadow, tab8, module, ".", mrename, "(self)\n", NIL);
	Printv(f_shadow, tab8, "return weakref.proxy(self)\n", NIL);
	Delete(mrename);
      }
    }
    return result;
  }

  /* ----------------------------------------------------------------------------
   * END of C++ Director Class modifications
   * ------------------------------------------------------------------------- */


  /* ------------------------------------------------------------
   * classDeclaration()
   * ------------------------------------------------------------ */

  virtual int classDeclaration(Node *n) {
    if (shadow && !Getattr(n, "feature:onlychildren")) {
      Node *mod = Getattr(n, "module");
      if (mod) {
	String *modname = Getattr(mod, "name");
	Node *options = Getattr(mod, "options");
	String *pkg = options ? Getattr(options, "package") : 0;
	String *sym = Getattr(n, "sym:name");
	String *importname = import_name_string(package, mainmodule, pkg, modname, sym);
	Setattr(n, "python:proxy", importname);
	Delete(importname);
      }
    }
    int result = Language::classDeclaration(n);
    return result;
  }

  /* ------------------------------------------------------------
   * classHandler()
   * ------------------------------------------------------------ */

  SwigType *add_explicit_scope(SwigType *s) {
    if (!Strstr(s, "::")) {
      return NewStringf("::%s", s);
    }
    return Copy(s);
  }

  void builtin_pre_decl(Node *n) {
    SwigType *name = Getattr(n, "name");
    SwigType *sname = add_explicit_scope(name);
    String *rname = SwigType_namestr(sname);
    String *mname = SwigType_manglestr(sname);

    Printf(f_init, "\n/* type '%s' */\n", rname);
    Printf(f_init, "    d = PyDict_New();\n");

    Delete(sname);
    Delete(rname);
    Delete(mname);
  }

  void builtin_post_decl(File *f, Node *n) {
    SwigType *name = Getattr(n, "name");
    SwigType *pname = Copy(name);
    SwigType_add_pointer(pname);
    String *symname = Getattr(n, "sym:name");
    SwigType *sname = add_explicit_scope(name);
    String *rname = SwigType_namestr(sname);
    String *mname = SwigType_manglestr(sname);
    String *pmname = SwigType_manglestr(pname);
    String *templ = NewStringf("SwigPyBuiltin_%s", mname);
    int funpack = fastunpack;

    if (have_builtin_static_member_method_callback) {
      Printf(f_init, "  SWIG_Python_FixMethods(SwigPyBuiltin_%s_methods, swig_const_table, swig_types, swig_type_initial);\n", mname);
    }

    Printv(f_init, "  builtin_base_count = 0;\n", NIL);
    List *baselist = Getattr(n, "bases");
    if (baselist) {
      int base_count = 0;
      for (Iterator b = First(baselist); b.item; b = Next(b)) {
	String *bname = Getattr(b.item, "name");
	if (!bname || GetFlag(b.item, "feature:ignore"))
	  continue;
	base_count++;
	String *base_name = Copy(bname);
	SwigType_add_pointer(base_name);
	String *base_mname = SwigType_manglestr(base_name);
	Printf(f_init, "  builtin_basetype = SWIG_MangledTypeQuery(\"%s\");\n", base_mname);
	Printv(f_init, "  if (builtin_basetype && builtin_basetype->clientdata && ((SwigPyClientData *) builtin_basetype->clientdata)->pytype) {\n", NIL);
	Printv(f_init, "    builtin_bases[builtin_base_count++] = ((SwigPyClientData *) builtin_basetype->clientdata)->pytype;\n", NIL);
	Printv(f_init, "  } else {\n", NIL);
	Printf(f_init, "    PyErr_SetString(PyExc_TypeError, \"Could not create type '%s' as base '%s' has not been initialized.\\n\");\n", symname, bname);
	Printv(f_init, "      return -1;\n", NIL);
	Printv(f_init, "  }\n", NIL);
	Delete(base_name);
	Delete(base_mname);
      }
      if (base_count > max_bases)
	max_bases = base_count;
    }
    Printv(f_init, "  builtin_bases[builtin_base_count] = NULL;\n", NIL);
    builtin_bases_needed = 1;

    // Check for non-public destructor, in which case tp_dealloc will issue
    // a warning and allow the memory to leak.  Any class that doesn't explicitly
    // have a private/protected destructor has an implicit public destructor.
    static String *tp_dealloc_bad = NewString("SwigPyBuiltin_BadDealloc");

    String *getset_name = NewStringf("%s_getset", templ);
    String *methods_name = NewStringf("%s_methods", templ);
    String *getset_def = NewString("");
    Printf(getset_def, "SWIGINTERN PyGetSetDef %s[] = {\n", getset_name);

    // All objects have 'this' and 'thisown' attributes
    Printv(f_init, "PyDict_SetItemString(d, \"this\", this_descr);\n", NIL);
    Printv(f_init, "PyDict_SetItemString(d, \"thisown\", thisown_descr);\n", NIL);

    // Now, the rest of the attributes
    for (Iterator member_iter = First(builtin_getset); member_iter.item; member_iter = Next(member_iter)) {
      String *memname = member_iter.key;
      Hash *mgetset = member_iter.item;
      String *getter = Getattr(mgetset, "getter");
      String *setter = Getattr(mgetset, "setter");
      const char *getter_closure = getter ? funpack ? "SwigPyBuiltin_FunpackGetterClosure" : "SwigPyBuiltin_GetterClosure" : "0";
      const char *setter_closure = setter ? funpack ? "SwigPyBuiltin_FunpackSetterClosure" : "SwigPyBuiltin_SetterClosure" : "0";
      String *gspair = NewStringf("%s_%s_getset", symname, memname);
      Printf(f, "static SwigPyGetSet %s = { %s, %s };\n", gspair, getter ? getter : "0", setter ? setter : "0");
      String *doc = Getattr(mgetset, "doc");
      if (!doc)
	doc = NewStringf("%s.%s", name, memname);
      String *entry = NewStringf("{ (char *)\"%s\", %s, %s, (char *)\"%s\", &%s }", memname, getter_closure, setter_closure, doc, gspair);
      if (GetFlag(mgetset, "static")) {
	Printf(f, "static PyGetSetDef %s_def = %s;\n", gspair, entry);
	Printf(f_init, "static_getset = SwigPyStaticVar_new_getset(metatype, &%s_def);\n", gspair);
	Printf(f_init, "PyDict_SetItemString(d, static_getset->d_getset->name, (PyObject *)static_getset);\n");
	Printf(f_init, "SWIG_Py_DECREF((PyObject *)static_getset);\n");
      } else {
	Printf(getset_def, "    %s,\n", entry);
      }
      Delete(gspair);
      Delete(entry);
    }
    Printv(f, getset_def, "    { NULL, NULL, NULL, NULL, NULL } /* Sentinel */\n", "};\n\n", NIL);

    // Rich compare function
    Hash *richcompare = Getattr(n, "python:richcompare");
    String *richcompare_func = NewStringf("%s_richcompare", templ);
    assert(richcompare);
    Printf(f, "SWIGINTERN PyObject *\n");
    Printf(f, "%s(PyObject *self, PyObject *other, int op) {\n", richcompare_func);
    Printf(f, "  PyObject *result = NULL;\n");
    if (!funpack) {
      Printf(f, "  PyObject *tuple = PyTuple_New(1);\n");
      Printf(f, "  assert(tuple);\n");
      Printf(f, "  PyTuple_SET_ITEM(tuple, 0, other);\n");
      Printf(f, "  SWIG_Py_XINCREF(other);\n");
    }
    List *richcompare_list = SortedKeys(richcompare, 0);
    Iterator rich_iter = First(richcompare_list);
    if (rich_iter.item) {
      Printf(f, "  switch (op) {\n");
      for (; rich_iter.item; rich_iter = Next(rich_iter))
	Printf(f, "    case %s : result = %s(self, %s); break;\n", rich_iter.item, Getattr(richcompare, rich_iter.item), funpack ? "other" : "tuple");
      Printv(f, "    default : break;\n", NIL);
      Printf(f, "  }\n");
    }
    Delete(richcompare_list);
    Printv(f, "  if (!result && !PyErr_Occurred()) {\n", NIL);
    Printv(f, "    if (SwigPyObject_Check(self) && SwigPyObject_Check(other)) {\n", NIL);
    Printv(f, "      result = SwigPyObject_richcompare(self, other, op);\n", NIL);
    Printv(f, "    } else {\n", NIL);
    Printv(f, "      result = Py_NotImplemented;\n", NIL);
    Printv(f, "      SWIG_Py_INCREF(result);\n", NIL);
    Printv(f, "    }\n", NIL);
    Printv(f, "  }\n", NIL);
    if (!funpack)
      Printf(f, "  SWIG_Py_DECREF(tuple);\n");
    Printf(f, "  return result;\n");
    Printf(f, "}\n\n");

    // Methods
    Printf(f, "SWIGINTERN PyMethodDef %s_methods[] = {\n", templ);
    Dump(builtin_methods, f);
    Printf(f, "  { NULL, NULL, 0, NULL } /* Sentinel */\n};\n\n");

    // No instance dict for nondynamic objects
    if (GetFlag(n, "feature:python:nondynamic"))
      Setattr(n, "feature:python:tp_setattro", "SWIG_Python_NonDynamicSetAttr");

    Node *mod = Getattr(n, "module");
    String *modname = mod ? Getattr(mod, "name") : 0;
    String *quoted_symname;
    if (package) {
      if (modname)
	quoted_symname = NewStringf("\"%s.%s.%s\"", package, modname, symname);
      else
	quoted_symname = NewStringf("\"%s.%s\"", package, symname);
    } else {
      if (modname)
	quoted_symname = NewStringf("\"%s.%s\"", modname, symname);
      else
	quoted_symname = NewStringf("\"%s\"", symname);
    }
    String *quoted_tp_doc_str = NewStringf("\"%s\"", getSlot(n, "feature:python:tp_doc"));
    String *tp_init = NewString(builtin_tp_init ? Char(builtin_tp_init) : Swig_directorclass(n) ? "0" : "SwigPyBuiltin_BadInit");
    String *tp_flags = NewString("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES");
    String *tp_flags_py3 = NewString("Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE");

    static String *tp_basicsize = NewStringf("sizeof(SwigPyObject)");
    static String *tp_dictoffset_default = NewString("offsetof(SwigPyObject, swigdict)");
    static String *tp_hash = NewString("SwigPyObject_hash");
    static String *tp_new = NewString("PyType_GenericNew");
    String *tp_as_number = NewStringf("&%s_type.as_number", templ);
    String *tp_as_sequence = NewStringf("&%s_type.as_sequence", templ);
    String *tp_as_mapping = NewStringf("&%s_type.as_mapping", templ);
    String *tp_as_buffer = NewStringf("&%s_type.as_buffer", templ);

    Printv(f, "#ifndef SWIG_HEAPTYPES\n", NIL);
    Printf(f, "static PyHeapTypeObject %s_type = {\n", templ);

    // PyTypeObject ht_type
    Printf(f, "  {\n");
    Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
    Printv(f, "    PyVarObject_HEAD_INIT(NULL, 0)\n", NIL);
    Printv(f, "#else\n", NIL);
    Printf(f, "    PyObject_HEAD_INIT(NULL)\n");
    printSlot(f, getSlot(), "ob_size");
    Printv(f, "#endif\n", NIL);
    printSlot(f, quoted_symname, "tp_name");
    printSlot(f, getSlot(n, "feature:python:tp_basicsize", tp_basicsize), "tp_basicsize");
    printSlot(f, getSlot(n, "feature:python:tp_itemsize"), "tp_itemsize");
    printSlot(f, getSlot(n, "feature:python:tp_dealloc", tp_dealloc_bad), "tp_dealloc", "destructor");
    Printv(f, "#if PY_VERSION_HEX < 0x030800b4\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_print"), "tp_print", "printfunc");
    Printv(f, "#else\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_vectorcall_offset"), "tp_vectorcall_offset", "Py_ssize_t");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_getattr"), "tp_getattr", "getattrfunc");
    printSlot(f, getSlot(n, "feature:python:tp_setattr"), "tp_setattr", "setattrfunc");
    Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_compare"), "tp_compare");
    Printv(f, "#else\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_compare"), "tp_compare", "cmpfunc");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_repr"), "tp_repr", "reprfunc");
    printSlot(f, getSlot(n, "feature:python:tp_as_number", tp_as_number), "tp_as_number");
    printSlot(f, getSlot(n, "feature:python:tp_as_sequence", tp_as_sequence), "tp_as_sequence");
    printSlot(f, getSlot(n, "feature:python:tp_as_mapping", tp_as_mapping), "tp_as_mapping");
    printSlot(f, getSlot(n, "feature:python:tp_hash", tp_hash), "tp_hash", "hashfunc");
    printSlot(f, getSlot(n, "feature:python:tp_call"), "tp_call", "ternaryfunc");
    printSlot(f, getSlot(n, "feature:python:tp_str"), "tp_str", "reprfunc");
    printSlot(f, getSlot(n, "feature:python:tp_getattro"), "tp_getattro", "getattrofunc");
    printSlot(f, getSlot(n, "feature:python:tp_setattro"), "tp_setattro", "setattrofunc");
    printSlot(f, getSlot(n, "feature:python:tp_as_buffer", tp_as_buffer), "tp_as_buffer");
    Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_flags", tp_flags_py3), "tp_flags");
    Printv(f, "#else\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_flags", tp_flags), "tp_flags");
    Printv(f, "#endif\n", NIL);
    if (have_docstring(n)) {
      String *ds = cdocstring(n, AUTODOC_CLASS);
      String *tp_doc = NewString("");
      Printf(tp_doc, "\"%s\"", ds);
      Delete(ds);
      printSlot(f, tp_doc, "tp_doc");
      Delete(tp_doc);
    } else {
      printSlot(f, quoted_tp_doc_str, "tp_doc");
    }
    printSlot(f, getSlot(n, "feature:python:tp_traverse"), "tp_traverse", "traverseproc");
    printSlot(f, getSlot(n, "feature:python:tp_clear"), "tp_clear", "inquiry");
    printSlot(f, getSlot(n, "feature:python:tp_richcompare", richcompare_func), "tp_richcompare", "richcmpfunc");
    printSlot(f, getSlot(n, "feature:python:tp_weaklistoffset"), "tp_weaklistoffset");
    printSlot(f, getSlot(n, "feature:python:tp_iter"), "tp_iter", "getiterfunc");
    printSlot(f, getSlot(n, "feature:python:tp_iternext"), "tp_iternext", "iternextfunc");
    printSlot(f, getSlot(n, "feature:python:tp_methods", methods_name), "tp_methods");
    printSlot(f, getSlot(n, "feature:python:tp_members"), "tp_members");
    printSlot(f, getSlot(n, "feature:python:tp_getset", getset_name), "tp_getset");
    printSlot(f, getSlot(n, "feature:python:tp_base"), "tp_base");
    printSlot(f, getSlot(n, "feature:python:tp_dict"), "tp_dict");
    printSlot(f, getSlot(n, "feature:python:tp_descr_get"), "tp_descr_get", "descrgetfunc");
    printSlot(f, getSlot(n, "feature:python:tp_descr_set"), "tp_descr_set", "descrsetfunc");
    printSlot(f, getSlot(n, "feature:python:tp_dictoffset", tp_dictoffset_default), "tp_dictoffset", "Py_ssize_t");
    printSlot(f, getSlot(n, "feature:python:tp_init", tp_init), "tp_init", "initproc");
    printSlot(f, getSlot(n, "feature:python:tp_alloc"), "tp_alloc", "allocfunc");
    printSlot(f, getSlot(), "tp_new", "newfunc");
    printSlot(f, getSlot(n, "feature:python:tp_free"), "tp_free", "freefunc");
    printSlot(f, getSlot(n, "feature:python:tp_is_gc"), "tp_is_gc", "inquiry");
    printSlot(f, getSlot(n, "feature:python:tp_bases"), "tp_bases", "PyObject *");
    printSlot(f, getSlot(n, "feature:python:tp_mro"), "tp_mro", "PyObject *");
    printSlot(f, getSlot(n, "feature:python:tp_cache"), "tp_cache", "PyObject *");
    printSlot(f, getSlot(n, "feature:python:tp_subclasses"), "tp_subclasses", "PyObject *");
    printSlot(f, getSlot(n, "feature:python:tp_weaklist"), "tp_weaklist", "PyObject *");
    printSlot(f, getSlot(n, "feature:python:tp_del"), "tp_del", "destructor");
    printSlot(f, getSlot(n, "feature:python:tp_version_tag"), "tp_version_tag", "int");
    Printv(f, "#if PY_VERSION_HEX >= 0x03040000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_finalize"), "tp_finalize", "destructor");
    Printv(f, "#endif\n", NIL);
    Printv(f, "#if PY_VERSION_HEX >= 0x03080000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_vectorcall"), "tp_vectorcall", "vectorcallfunc");
    Printv(f, "#endif\n", NIL);
    Printv(f, "#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000)\n", NIL);
    printSlot(f, getSlot(), "tp_print");
    Printv(f, "#endif\n", NIL);
    Printv(f, "#if PY_VERSION_HEX >= 0x030c0000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_watched"), "tp_watched", "unsigned char");
    Printv(f, "#endif\n", NIL);
    Printv(f, "#if PY_VERSION_HEX >= 0x030d00a4\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_versions_used"), "tp_versions_used", "uint16_t");
    Printv(f, "#endif\n", NIL);

    Printv(f, "#ifdef COUNT_ALLOCS\n", NIL);
    printSlot(f, getSlot(n, "feature:python:tp_allocs"), "tp_allocs", "Py_ssize_t");
    printSlot(f, getSlot(n, "feature:python:tp_frees"), "tp_frees", "Py_ssize_t");
    printSlot(f, getSlot(n, "feature:python:tp_maxalloc"), "tp_maxalloc", "Py_ssize_t");
    printSlot(f, getSlot(n, "feature:python:tp_prev"), "tp_prev");
    printSlot(f, getSlot(n, "feature:python:tp_next"), "tp_next");
    Printv(f, "#endif\n", NIL);
    Printf(f, "  },\n");

    // PyAsyncMethods as_async
    Printv(f, "#if PY_VERSION_HEX >= 0x03050000\n", NIL);
    Printf(f, "  {\n");
    printSlot(f, getSlot(n, "feature:python:am_await"), "am_await", "unaryfunc");
    printSlot(f, getSlot(n, "feature:python:am_aiter"), "am_aiter", "unaryfunc");
    printSlot(f, getSlot(n, "feature:python:am_anext"), "am_anext", "unaryfunc");
    Printv(f, "# if PY_VERSION_HEX >= 0x030a0000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:am_send"), "am_send", "sendfunc");
    Printv(f, "# endif\n", NIL);
    Printf(f, "  },\n");
    Printv(f, "#endif\n", NIL);

    // PyNumberMethods as_number
    Printf(f, "  {\n");
    printSlot(f, getSlot(n, "feature:python:nb_add"), "nb_add", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_subtract"), "nb_subtract", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_multiply"), "nb_multiply", "binaryfunc");
    Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_divide"), "nb_divide", "binaryfunc");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_remainder"), "nb_remainder", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_divmod"), "nb_divmod", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_power"), "nb_power", "ternaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_negative"), "nb_negative", "unaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_positive"), "nb_positive", "unaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_absolute"), "nb_absolute", "unaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_nonzero"), "nb_nonzero", "inquiry");
    printSlot(f, getSlot(n, "feature:python:nb_invert"), "nb_invert", "unaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_lshift"), "nb_lshift", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_rshift"), "nb_rshift", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_and"), "nb_and", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_xor"), "nb_xor", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_or"), "nb_or", "binaryfunc");
    Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_coerce"), "nb_coerce", "coercion");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_int"), "nb_int", "unaryfunc");
    Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_reserved"), "nb_reserved", "void *");
    Printv(f, "#else\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_long"), "nb_long", "unaryfunc");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_float"), "nb_float", "unaryfunc");
    Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_oct"), "nb_oct", "unaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_hex"), "nb_hex", "unaryfunc");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_inplace_add"), "nb_inplace_add", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_subtract"), "nb_inplace_subtract", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_multiply"), "nb_inplace_multiply", "binaryfunc");
    Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_inplace_divide"), "nb_inplace_divide", "binaryfunc");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_inplace_remainder"), "nb_inplace_remainder", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_power"), "nb_inplace_power", "ternaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_lshift"), "nb_inplace_lshift", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_rshift"), "nb_inplace_rshift", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_and"), "nb_inplace_and", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_xor"), "nb_inplace_xor", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_or"), "nb_inplace_or", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_floor_divide"), "nb_floor_divide", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_divide"), "nb_true_divide", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_floor_divide"), "nb_inplace_floor_divide", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_divide"), "nb_inplace_true_divide", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_index"), "nb_index", "unaryfunc");
    Printv(f, "#if PY_VERSION_HEX >= 0x03050000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:nb_matrix_multiply"), "nb_matrix_multiply", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:nb_inplace_matrix_multiply"), "nb_inplace_matrix_multiply", "binaryfunc");
    Printv(f, "#endif\n", NIL);
    Printf(f, "  },\n");

    // PyMappingMethods as_mapping;
    Printf(f, "  {\n");
    printSlot(f, getSlot(n, "feature:python:mp_length"), "mp_length", "lenfunc");
    printSlot(f, getSlot(n, "feature:python:mp_subscript"), "mp_subscript", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:mp_ass_subscript"), "mp_ass_subscript", "objobjargproc");
    Printf(f, "  },\n");

    // PySequenceMethods as_sequence;
    Printf(f, "  {\n");
    printSlot(f, getSlot(n, "feature:python:sq_length"), "sq_length", "lenfunc");
    printSlot(f, getSlot(n, "feature:python:sq_concat"), "sq_concat", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:sq_repeat"), "sq_repeat", "ssizeargfunc");
    printSlot(f, getSlot(n, "feature:python:sq_item"), "sq_item", "ssizeargfunc");
    Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:was_sq_slice"), "was_sq_slice", "void *");
    Printv(f, "#else\n", NIL);
    printSlot(f, getSlot(n, "feature:python:sq_slice"), "sq_slice", "ssizessizeargfunc");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:sq_ass_item"), "sq_ass_item", "ssizeobjargproc");
    Printv(f, "#if PY_VERSION_HEX >= 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:was_sq_ass_slice"), "was_sq_ass_slice", "void *");
    Printv(f, "#else\n", NIL);
    printSlot(f, getSlot(n, "feature:python:sq_ass_slice"), "sq_ass_slice", "ssizessizeobjargproc");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:sq_contains"), "sq_contains", "objobjproc");
    printSlot(f, getSlot(n, "feature:python:sq_inplace_concat"), "sq_inplace_concat", "binaryfunc");
    printSlot(f, getSlot(n, "feature:python:sq_inplace_repeat"), "sq_inplace_repeat", "ssizeargfunc");
    Printf(f, "  },\n");

    // PyBufferProcs as_buffer;
    Printf(f, "  {\n");
    Printv(f, "#if PY_VERSION_HEX < 0x03000000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:bf_getreadbuffer"), "bf_getreadbuffer", "readbufferproc");
    printSlot(f, getSlot(n, "feature:python:bf_getwritebuffer"), "bf_getwritebuffer", "writebufferproc");
    printSlot(f, getSlot(n, "feature:python:bf_getsegcount"), "bf_getsegcount", "segcountproc");
    printSlot(f, getSlot(n, "feature:python:bf_getcharbuffer"), "bf_getcharbuffer", "charbufferproc");
    Printv(f, "#endif\n", NIL);
    printSlot(f, getSlot(n, "feature:python:bf_getbuffer"), "bf_getbuffer", "getbufferproc");
    printSlot(f, getSlot(n, "feature:python:bf_releasebuffer"), "bf_releasebuffer", "releasebufferproc");
    Printf(f, "  },\n");

    // PyObject *ht_name, *ht_slots, *ht_qualname;
    printSlot(f, getSlot(n, "feature:python:ht_name"), "ht_name", "PyObject *");
    printSlot(f, getSlot(n, "feature:python:ht_slots"), "ht_slots", "PyObject *");
    Printv(f, "#if PY_VERSION_HEX >= 0x03030000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:ht_qualname"), "ht_qualname", "PyObject *");

    // struct _dictkeysobject *ht_cached_keys;
    printSlot(f, getSlot(n, "feature:python:ht_cached_keys"), "ht_cached_keys");
    Printv(f, "#endif\n", NIL);

    // PyObject *ht_module;
    Printv(f, "#if PY_VERSION_HEX >= 0x03090000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:ht_module"), "ht_module", "PyObject *");
    Printv(f, "#endif\n", NIL);

    // char *_ht_tpname;
    Printv(f, "#if PY_VERSION_HEX >= 0x030b0000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:_ht_tpname"), "_ht_tpname", "char *");

    // void *ht_token;
    Printv(f, "#if PY_VERSION_HEX >= 0x030e0000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:ht_token"), "ht_token", "void *");
    Printv(f, "#endif\n", NIL);

    // struct _specialization_cache _spec_cache;
    Printf(f, "  {\n");
    printSlot(f, getSlot(n, "feature:python:getitem"), "getitem", "PyObject *");
    Printv(f, "#if PY_VERSION_HEX >= 0x030c0000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:getitem_version"), "getitem_version", "uint32_t");
    Printv(f, "#endif\n", NIL);
    Printv(f, "#if PY_VERSION_HEX >= 0x030d0000\n", NIL);
    printSlot(f, getSlot(n, "feature:python:init"), "init", "PyObject *");
    Printv(f, "#endif\n", NIL);
    Printf(f, "  }\n");
    Printv(f, "#endif\n", NIL);
    Printf(f, "};\n\n");
    Printf(f, "static PyTypeObject *%s_type_create(PyTypeObject *type, PyTypeObject **bases, PyObject *dict) {\n", templ);
    Printv(f, "  PyObject *tuple_bases;\n", NIL);
    Printf(f, "  PyTypeObject *pytype = (PyTypeObject *)&%s_type;\n", templ);
    Printf(f, "  pytype->tp_dict = dict;\n");
    Printv(f, "  SwigPyBuiltin_SetMetaType(pytype, type);\n", NIL);
    Printf(f, "  pytype->tp_new = %s;\n", getSlot(n, "feature:python:tp_new", tp_new));
    Printv(f, "  tuple_bases = SwigPyBuiltin_InitBases(bases);\n", NIL);
    Printv(f, "  pytype->tp_base = bases[0];\n", NIL);
    Printv(f, "  SWIG_Py_INCREF(pytype->tp_base);\n", NIL);
    Printv(f, "  pytype->tp_bases = tuple_bases;\n", NIL);
    Printv(f, "  if (PyType_Ready(pytype) < 0) {\n", NIL);
    Printv(f, "    return NULL;\n", NIL);
    Printv(f, "  }\n", NIL);
    Printf(f, "  return pytype;\n");
    Printf(f, "}\n\n");

    Printv(f, "#else\n", NIL);

    Printf(f, "static PyTypeObject *%s_type_create(PyTypeObject *type, PyTypeObject **bases, PyObject *dict) {\n", templ);

    // All heap types slots
    Printf(f, "  PyType_Slot slots[] = {\n");

    // tp slots
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_init", tp_init), "tp_init", "initproc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_dealloc", tp_dealloc_bad), "tp_dealloc", "destructor");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_alloc"), "tp_alloc", "allocfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_new"), "tp_new", "newfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_free"), "tp_free", "freefunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_is_gc"), "tp_is_gc", "inquiry");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_finalize"), "tp_finalize", "destructor");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_vectorcall"), "tp_vectorcall", "vectorcallfunc");

    if (have_docstring(n)) {
      String *ds = cdocstring(n, AUTODOC_CLASS);
      String *tp_doc = NewString("");
      Printf(tp_doc, "\"%s\"", ds);
      Delete(ds);
      printHeapTypesSlot(f, tp_doc, "tp_doc");
      Delete(tp_doc);
    } else {
      printHeapTypesSlot(f, quoted_tp_doc_str, "tp_doc");
    }

    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_repr"), "tp_repr", "reprfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_str"), "tp_str", "reprfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_traverse"), "tp_traverse", "traverseproc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_clear"), "tp_clear", "inquiry");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_richcompare", richcompare_func), "tp_richcompare", "richcmpfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_methods", methods_name), "tp_methods");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_members"), "tp_members");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_getset", getset_name), "tp_getset");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_hash", tp_hash), "tp_hash", "hashfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_call"), "tp_call", "ternaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_getattro"), "tp_getattro", "getattrofunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_setattro"), "tp_setattro", "setattrofunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_descr_get"), "tp_descr_get", "descrgetfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_descr_set"), "tp_descr_set", "descrsetfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_iter"), "tp_iter", "getiterfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:tp_iternext"), "tp_iternext", "iternextfunc");

    // effectively deprecated slots, see https://docs.python.org/3/c-api/typeobj.html:
    //  tp_getattr
    //  tp_setattr
    //  tp_weaklistoffset
    //  tp_dictoffset
    //  tp_del

    // async method slots
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:am_await"), "am_await", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:am_aiter"), "am_aiter", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:am_anext"), "am_anext", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:am_send"), "am_send", "sendfunc");

    // number slots
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_add"), "nb_add", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_subtract"), "nb_subtract", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_multiply"), "nb_multiply", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_remainder"), "nb_remainder", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_divmod"), "nb_divmod", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_power"), "nb_power", "ternaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_negative"), "nb_negative", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_positive"), "nb_positive", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_absolute"), "nb_absolute", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_nonzero"), "nb_bool", "inquiry");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_invert"), "nb_invert", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_lshift"), "nb_lshift", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_rshift"), "nb_rshift", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_and"), "nb_and", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_xor"), "nb_xor", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_or"), "nb_or", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_int"), "nb_int", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_float"), "nb_float", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_add"), "nb_inplace_add", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_subtract"), "nb_inplace_subtract", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_multiply"), "nb_inplace_multiply", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_remainder"), "nb_inplace_remainder", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_power"), "nb_inplace_power", "ternaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_lshift"), "nb_inplace_lshift", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_rshift"), "nb_inplace_rshift", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_and"), "nb_inplace_and", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_xor"), "nb_inplace_xor", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_or"), "nb_inplace_or", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_floor_divide"), "nb_floor_divide", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_divide"), "nb_true_divide", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_floor_divide"), "nb_inplace_floor_divide", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_divide"), "nb_inplace_true_divide", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_index"), "nb_index", "unaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_matrix_multiply"), "nb_matrix_multiply", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:nb_inplace_matrix_multiply"), "nb_inplace_matrix_multiply", "binaryfunc");

    // mapping slots
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:mp_length"), "mp_length", "lenfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:mp_subscript"), "mp_subscript", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:mp_ass_subscript"), "mp_ass_subscript", "objobjargproc");

    // sequence slots
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:sq_length"), "sq_length", "lenfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:sq_concat"), "sq_concat", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:sq_repeat"), "sq_repeat", "ssizeargfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:sq_item"), "sq_item", "ssizeargfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:sq_ass_item"), "sq_ass_item", "ssizeobjargproc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:sq_contains"), "sq_contains", "objobjproc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:sq_inplace_concat"), "sq_inplace_concat", "binaryfunc");
    printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:sq_inplace_repeat"), "sq_inplace_repeat", "ssizeargfunc");

    // buffer slots
    String *bf_getbuffer = Getattr(n, "feature:python:bf_getbuffer");
    String *bf_releasebuffer = Getattr(n, "feature:python:bf_releasebuffer");
    if (bf_getbuffer || bf_releasebuffer) {
      Printv(f, "#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x03090000 || Py_LIMITED_API+0 >= 0x030b0000\n", NIL);
      printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:bf_getbuffer"), "bf_getbuffer", "getbufferproc");
      printHeapTypesSlot(f, getHeapTypesSlot(n, "feature:python:bf_releasebuffer"), "bf_releasebuffer", "releasebufferproc");
      Printv(f, "#endif\n", NIL);
    }

    Printf(f, "    { 0, NULL }\n");
    Printf(f, "  };\n");
    Printf(f, "  PyType_Spec spec = {\n");
    Printf(f, "    %s,\n", quoted_symname);
    Printf(f, "    sizeof(SwigPyObject),\n");
    Printf(f, "    0,\n");
    Printf(f, "    %s,\n", getHeapTypesSlot(n, "feature:python:tp_flags", tp_flags_py3), "tp_flags");
    Printf(f, "    slots\n");
    Printf(f, "  };\n");
    Printv(f, "  PyObject *tuple_bases = SwigPyBuiltin_InitBases(bases);\n", NIL);
    Printf(f, "  PyTypeObject *pytype = (PyTypeObject *)PyType_FromSpecWithBases(&spec, tuple_bases);\n");
    if (bf_getbuffer || bf_releasebuffer) {
      Printv(f, "#if !defined(Py_LIMITED_API) && PY_VERSION_HEX < 0x03090000\n", NIL);
      Printf(f, "  if (pytype) {\n");
      if (bf_getbuffer)
	Printf(f, "    pytype->tp_as_buffer->bf_getbuffer = %s;\n", getSlot(n, "feature:python:bf_getbuffer"));
      if (bf_releasebuffer)
        Printf(f, "    pytype->tp_as_buffer->bf_releasebuffer = %s;\n", getSlot(n, "feature:python:bf_releasebuffer"));
      Printf(f, "  }\n");
      Printv(f, "#endif\n", NIL);
    }
    Printf(f, "  if (pytype) {\n");
    Printf(f, "    if (PyDict_Merge(pytype->tp_dict, dict, 1) == 0) {\n");
    Printv(f, "      SwigPyBuiltin_SetMetaType(pytype, type);\n", NIL);
    Printf(f, "      PyType_Modified(pytype);\n");
    Printf(f, "    } else {\n");
    Printf(f, "      pytype = 0;\n");
    Printf(f, "    }\n");
    Printf(f, "  }\n");
    Printf(f, "  Py_DECREF(dict);\n");
    Printf(f, "  return pytype;\n");

    Printv(f, "}\n", NULL);

    Printv(f, "#endif\n", NIL);

    String *clientdata = NewString("");
    Printf(clientdata, "&%s_clientdata", templ);
    SwigType_remember_mangleddata(pmname, clientdata);

    SwigType *smart = Getattr(n, "smart");
    if (smart) {
      SwigType *psmart = Copy(smart);
      SwigType_add_pointer(psmart);
      String *smart_pmname = SwigType_manglestr(psmart);
      SwigType_remember_mangleddata(smart_pmname, clientdata);
      Delete(smart_pmname);
      Delete(psmart);
    }

    Printf(f, "SWIGINTERN SwigPyClientData %s_clientdata = {0, 0, 0, 0, 0, 0, 0};\n\n", templ);

    Printf(f_init, "    builtin_pytype = %s_type_create(metatype, builtin_bases, d);\n", templ);
    Printf(f_init, "    if(!builtin_pytype) {\n", templ);
    Printv(f_init, "      return -1;\n", NIL);
    Printv(f_init, "    }\n", NIL);
    Printf(f_init, "    SwigPyBuiltin_%s_clientdata.pytype = builtin_pytype;\n", mname);
    if (GetFlag(n, "feature:implicitconv")) {
      Printf(f_init, "    SwigPyBuiltin_%s_clientdata.klass = (PyObject *)builtin_pytype;\n", mname);
    }
    Printv(f_init, "    SWIG_Py_INCREF((PyObject *)builtin_pytype);\n", NIL);
    Printf(f_init, "    if (PyModule_AddObject(m, \"%s\", (PyObject *)builtin_pytype) != 0) {\n", symname);
    Printf(f_init, "      SWIG_Py_DECREF((PyObject *)builtin_pytype);\n");
    Printv(f_init, "      return -1;\n", NIL);
    Printf(f_init, "    }\n", symname);
    Printf(f_init, "    SwigPyBuiltin_AddPublicSymbol(public_interface, \"%s\");\n", symname);
    Printv(f_init, "    d = md;\n", NIL);

    Delete(clientdata);
    Delete(sname);
    Delete(rname);
    Delete(mname);
    Delete(pname);
    Delete(pmname);
    Delete(templ);
    Delete(tp_flags);
    Delete(tp_flags_py3);
    Delete(tp_as_buffer);
    Delete(tp_as_mapping);
    Delete(tp_as_sequence);
    Delete(tp_as_number);
    Delete(quoted_symname);
    Delete(quoted_tp_doc_str);
    Delete(tp_init);
    Delete(richcompare_func);
    Delete(getset_name);
    Delete(methods_name);
  }

  virtual int classHandler(Node *n) {
    File *f_shadow_file = f_shadow;
    Node *base_node = NULL;

    if (shadow) {

      /* Create new strings for building up a wrapper function */
      have_constructor = 0;
      have_repr = 0;
      have_builtin_static_member_method_callback = false;

      class_name = Getattr(n, "sym:name");
      real_classname = Getattr(n, "name");

      if (!addSymbol(class_name, n))
	return SWIG_ERROR;

      if (builtin) {
	List *baselist = Getattr(n, "bases");
	if (baselist && Len(baselist) > 0) {
	  Iterator b = First(baselist);
	  base_node = b.item;
	}
      }

      shadow_indent = (String *) tab4;

      /* Handle inheritance */
      String *base_class = NewString("");
      List *baselist = Getattr(n, "bases");
      if (baselist && Len(baselist)) {
	Iterator b;
	b = First(baselist);
	while (b.item) {
	  String *bname = Getattr(b.item, "python:proxy");
	  bool ignore = GetFlag(b.item, "feature:ignore") ? true : false;
	  if (!bname || ignore) {
	    if (!bname && !ignore) {
	      Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(n), Getline(n),
			   "Base class '%s' ignored - unknown module name for base. Either import the appropriate module interface file or specify the name of the module in the %%import directive.\n",
			   SwigType_namestr(Getattr(b.item, "name")));
	    }
	    b = Next(b);
	    continue;
	  }
	  Printv(base_class, bname, NIL);
	  b = Next(b);
	  if (b.item) {
            Printv(base_class, ", ", NIL);
	  }
	}
      }

      if (builtin) {
	Hash *base_richcompare = NULL;
	Hash *richcompare = NULL;
	if (base_node)
	  base_richcompare = Getattr(base_node, "python:richcompare");
	if (base_richcompare)
	  richcompare = Copy(base_richcompare);
	else
	  richcompare = NewHash();
	Setattr(n, "python:richcompare", richcompare);
      }

      /* dealing with abstract base class */
      String *abcs = Getattr(n, "feature:python:abc");
      if (abcs) {
	if (Len(base_class) > 0)
	  Printv(base_class, ", ", NIL);
	Printv(base_class, abcs, NIL);
      }

      if (builtin) {
	if (have_docstring(n)) {
	  String *ds = cdocstring(n, AUTODOC_CLASS);
	  Setattr(n, "feature:python:tp_doc", ds);
	  Delete(ds);
	} else {
	  SwigType *name = Getattr(n, "name");
	  SwigType *sname = add_explicit_scope(name);
	  String *rname = SwigType_namestr(sname);
	  Setattr(n, "feature:python:tp_doc", rname);
	  Delete(sname);
	  Delete(rname);
	}
      } else {
	if (GetFlag(n, "feature:python:nondynamic"))
	  Printv(f_shadow, "@_swig_add_metaclass(_SwigNonDynamicMeta)\n", NIL);
	Printv(f_shadow, "class ", class_name, NIL);

	if (Len(base_class)) {
	  Printf(f_shadow, "(%s)", base_class);
	} else {
	  if (GetFlag(n, "feature:exceptionclass")) {
	    Printf(f_shadow, "(Exception)");
	  } else {
	    Printf(f_shadow, "(object");
	    /* Replace @_swig_add_metaclass above with below when support for python 2.7 is dropped
	    if (GetFlag(n, "feature:python:nondynamic")) {
	      Printf(f_shadow, ", metaclass=_SwigNonDynamicMeta");
	    }
	    */
	    Printf(f_shadow, ")");
	  }
	}

	Printf(f_shadow, ":\n");

	// write docstrings if requested
	if (have_docstring(n)) {
	  String *str = docstring(n, AUTODOC_CLASS, tab4);
	  if (str && Len(str))
	    Printv(f_shadow, tab4, str, "\n\n", NIL);
	}

	Printv(f_shadow, tab4, "thisown = property(lambda x: x.this.own(), ", "lambda x, v: x.this.own(v), doc=\"The membership flag\")\n", NIL);
	/* Add static attribute */
	if (GetFlag(n, "feature:python:nondynamic")) {
	  Printv(f_shadow_file, tab4, "__setattr__ = _swig_setattr_nondynamic_instance_variable(object.__setattr__)\n", NIL);
	}
      }
    }

    /* Emit all of the members */

    in_class = 1;
    if (builtin)
      builtin_pre_decl(n);

    /* Override the shadow file so we can capture its methods */
    f_shadow = NewString("");

    // Set up type check for director class constructor
    Clear(none_comparison);
    if (builtin && Swig_directorclass(n)) {
      String *p_real_classname = Copy(real_classname);
      SwigType_add_pointer(p_real_classname);
      String *mangle = SwigType_manglestr(p_real_classname);
      String *descriptor = NewStringf("SWIGTYPE%s", mangle);
      Printv(none_comparison, "Py_TYPE(self) != ((SwigPyClientData *)(", descriptor, ")->clientdata)->pytype", NIL);
      Delete(descriptor);
      Delete(mangle);
      Delete(p_real_classname);
    } else {
      Printv(none_comparison, "$arg != Py_None", NIL);
    }

    Language::classHandler(n);

    in_class = 0;

    /* Complete the class */
    if (shadow) {
      /* Generate a class registration function */
      // Replace storing a pointer to underlying class with a smart pointer (intended for use with non-intrusive smart pointers)
      SwigType *smart = Getattr(n, "smart");
      SwigType *ct = Copy(smart ? smart : real_classname);
      SwigType_add_pointer(ct);
      SwigType *realct = Copy(real_classname);
      SwigType_add_pointer(realct);
      SwigType_remember(realct);
      if (builtin) {
	Printv(f_wrappers, builtin_closures_code, NIL);
	Delete(builtin_closures_code);
	builtin_closures_code = NewString("");
	Clear(builtin_closures);
      } else {
	Printv(f_wrappers, "SWIGINTERN PyObject *", class_name, "_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {\n", NIL);
	Printv(f_wrappers, "  PyObject *obj = NULL;\n", NIL);
	Printv(f_wrappers, "  if (!SWIG_Python_UnpackTuple(args, \"swigregister\", 1, 1, &obj)) return NULL;\n", NIL);

	Printv(f_wrappers,
	       "  SWIG_TypeNewClientData(SWIGTYPE", SwigType_manglestr(ct), ", SWIG_NewClientData(obj));\n", "  return SWIG_Py_Void();\n", "}\n\n", NIL);
	String *cname = NewStringf("%s_swigregister", class_name);
	add_method(cname, cname, 0, 0, 1, 1, 1);
	Delete(cname);
      }
      Delete(ct);
      Delete(realct);
      if (!have_constructor) {
	if (!builtin)
	  Printv(f_shadow_file, "\n", tab4, "def __init__(self, *args, **kwargs):\n", tab8, "raise AttributeError(\"", "No constructor defined",
		 (Getattr(n, "abstracts") ? " - class is abstract" : ""), "\")\n", NIL);
      } else if (!builtin) {

	Printv(f_wrappers, "SWIGINTERN PyObject *", class_name, "_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {\n", NIL);
	Printv(f_wrappers, "  return SWIG_Python_InitShadowInstance(args);\n", "}\n\n", NIL);
	String *cname = NewStringf("%s_swiginit", class_name);
	add_method(cname, cname, 0);
	Delete(cname);
      }
      if (!have_repr && !builtin) {
	/* Supply a repr method for this class  */
	String *rname = SwigType_namestr(real_classname);
	Printv(f_shadow_file, tab4, "__repr__ = _swig_repr\n", NIL);
	Delete(rname);
      }

      if (builtin)
	builtin_post_decl(f_builtins, n);

      if (builtin_tp_init) {
	Delete(builtin_tp_init);
	builtin_tp_init = 0;
      }

      if (!builtin) {
	/* Now emit methods */
	Printv(f_shadow_file, f_shadow, NIL);
	Printf(f_shadow_file, "\n");
	Printf(f_shadow_file, "# Register %s in %s:\n", class_name, module);
	Printf(f_shadow_file, "%s.%s_swigregister(%s)\n", module, class_name, class_name);
      }

      shadow_indent = 0;
      if (Len(f_shadow_stubs) > 0)
	Printf(f_shadow_file, "%s\n", f_shadow_stubs);
      Clear(f_shadow_stubs);
    }

    if (builtin) {
      Clear(class_members);
      Clear(builtin_getset);
      Clear(builtin_methods);
    }

    /* Restore shadow file back to original version */
    Delete(f_shadow);
    f_shadow = f_shadow_file;

    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * functionHandler()  -  Mainly overloaded for callback handling
   * ------------------------------------------------------------ */

  virtual int functionHandler(Node *n) {
    String *pcb = GetFlagAttr(n, "feature:python:callback");
    if (pcb) {
      if (Strcmp(pcb, "1") == 0) {
	SetFlagAttr(n, "feature:callback", "%s_cb_ptr");
      } else {
	SetFlagAttr(n, "feature:callback", pcb);
      }
      autodoc_l dlevel = autodoc_level(Getattr(n, "feature:autodoc"));
      if (dlevel != NO_AUTODOC && dlevel > TYPES_AUTODOC) {
	Setattr(n, "feature:autodoc", "1");
      }
    }
    return Language::functionHandler(n);
  }

  /* ------------------------------------------------------------
   * memberfunctionHandler()
   * ------------------------------------------------------------ */

  virtual int memberfunctionHandler(Node *n) {
    String *symname = Getattr(n, "sym:name");
    int oldshadow;

    if (builtin)
      Swig_save("builtin_memberfunc", n, "python:argcount", NIL);

    /* Create the default member function */
    oldshadow = shadow;		/* Disable shadowing when wrapping member functions */
    if (shadow)
      shadow = shadow | PYSHADOW_MEMBER;
    Language::memberfunctionHandler(n);
    shadow = oldshadow;

    if (builtin && in_class) {
      // Can't use checkAttribute(n, "access", "public") because
      // "access" attr isn't set on %extend methods
      if (!checkAttribute(n, "access", "private") && strncmp(Char(symname), "operator ", 9) && !Getattr(class_members, symname)) {
	String *fullname = Swig_name_member(NSPACE_TODO, class_name, symname);
	String *wname = Swig_name_wrapper(fullname);
	Setattr(class_members, symname, n);
	int argcount = Getattr(n, "python:argcount") ? atoi(Char(Getattr(n, "python:argcount"))) : 2;
	String *ds = have_docstring(n) ? cdocstring(n, AUTODOC_METHOD) : NewString("");
	if (check_kwargs(n)) {
	  // Cast via void(*)(void) to suppress GCC -Wcast-function-type
	  // warning.  Python should always call the function correctly, but
	  // the Python C API requires us to store it in function pointer of a
	  // different type.
	  Printf(builtin_methods, "  { \"%s\", (PyCFunction)(void(*)(void))%s, METH_VARARGS|METH_KEYWORDS, \"%s\" },\n", symname, wname, ds);
	} else if (argcount == 0) {
	  Printf(builtin_methods, "  { \"%s\", %s, METH_NOARGS, \"%s\" },\n", symname, wname, ds);
	} else if (argcount == 1) {
	  Printf(builtin_methods, "  { \"%s\", %s, METH_O, \"%s\" },\n", symname, wname, ds);
	} else {
	  Printf(builtin_methods, "  { \"%s\", %s, METH_VARARGS, \"%s\" },\n", symname, wname, ds);
	}
	Delete(fullname);
	Delete(wname);
	Delete(ds);
      }
    }

    if (builtin)
      Swig_restore(n);

    if (!Getattr(n, "sym:nextSibling")) {
      if (shadow && !builtin) {
	int fproxy = fastproxy;
	String *fullname = Swig_name_member(NSPACE_TODO, class_name, symname);
	if (Strcmp(symname, "__repr__") == 0) {
	  have_repr = 1;
	}
	if (Getattr(n, "feature:shadow")) {
	  String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n), "%feature(\"shadow\")");
	  String *pyaction = NewStringf("%s.%s", module, fullname);
	  Replaceall(pycode, "$action", pyaction);
	  Delete(pyaction);
	  Printv(f_shadow, pycode, "\n", NIL);
	  Delete(pycode);
	  fproxy = 0;
	} else {
	  int allow_kwargs = (check_kwargs(n) && !Getattr(n, "sym:overloaded")) ? 1 : 0;
	  String *parms = make_pyParmList(n, true, false, allow_kwargs);
	  String *callParms = make_pyParmList(n, true, true, allow_kwargs);
	  if (!have_addtofunc(n)) {
	    if (!fastproxy || olddefs) {
	      Printv(f_shadow, "\n", tab4, "def ", symname, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
	      if (Node *node_with_doc = find_overload_with_docstring(n))
		Printv(f_shadow, tab8, docstring(node_with_doc, AUTODOC_METHOD, tab8), "\n", NIL);
	      Printv(f_shadow, tab8, "return ", funcCall(fullname, callParms), "\n", NIL);
	    }
	  } else {
	    Printv(f_shadow, "\n", tab4, "def ", symname, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
	    if (Node *node_with_doc = find_overload_with_docstring(n))
	      Printv(f_shadow, tab8, docstring(node_with_doc, AUTODOC_METHOD, tab8), "\n", NIL);
	    if (have_pythonprepend(n)) {
	      fproxy = 0;
	      Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
	    }
	    if (have_pythonappend(n)) {
	      fproxy = 0;
	      Printv(f_shadow, tab8, "val = ", funcCall(fullname, callParms), "\n", NIL);
	      Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
	      Printv(f_shadow, tab8, "return val\n\n", NIL);
	    } else {
	      Printv(f_shadow, tab8, "return ", funcCall(fullname, callParms), "\n\n", NIL);
	    }
	  }
	}
	if (fproxy) {
	  Printf(f_shadow, tab4);
	  Printf(f_shadow, "%s = _swig_new_instance_method(%s.%s)\n", symname, module, Swig_name_member(NSPACE_TODO, class_name, symname));
	}
	Delete(fullname);
      }
    }
    return SWIG_OK;
  }


  /* ------------------------------------------------------------
   * staticmemberfunctionHandler()
   * ------------------------------------------------------------ */

  virtual int staticmemberfunctionHandler(Node *n) {
    String *symname = Getattr(n, "sym:name");
    if (builtin && in_class) {
      Swig_save("builtin_memberconstantHandler", n, "pybuiltin:symname", NIL);
      Setattr(n, "pybuiltin:symname", symname);
    }
    Language::staticmemberfunctionHandler(n);
    if (builtin && in_class) {
      Swig_restore(n);
    }

    int kw = (check_kwargs(n) && !Getattr(n, "sym:overloaded")) ? 1 : 0;
    if (builtin && in_class) {
      if ((GetFlagAttr(n, "feature:extend") || checkAttribute(n, "access", "public"))
	  && !Getattr(class_members, symname)) {
	String *fullname = Swig_name_member(NSPACE_TODO, class_name, symname);
	String *wname = Swig_name_wrapper(fullname);
	Setattr(class_members, symname, n);
	int funpack = fastunpack && !Getattr(n, "sym:overloaded");
	String *pyflags = NewString("METH_STATIC|");
	int argcount = Getattr(n, "python:argcount") ? atoi(Char(Getattr(n, "python:argcount"))) : 2;
	if (funpack && argcount == 0)
	  Append(pyflags, "METH_NOARGS");
	else if (funpack && argcount == 1)
	  Append(pyflags, "METH_O");
	else
	  Append(pyflags, kw ? "METH_VARARGS|METH_KEYWORDS" : "METH_VARARGS");
	// Cast via void(*)(void) to suppress GCC -Wcast-function-type warning.
	// Python should always call the function correctly, but the Python C
	// API requires us to store it in function pointer of a different type.
	if (have_docstring(n)) {
	  String *ds = cdocstring(n, AUTODOC_STATICFUNC);
	  Printf(builtin_methods, "  { \"%s\", (PyCFunction)(void(*)(void))%s, %s, \"%s\" },\n", symname, wname, pyflags, ds);
	  Delete(ds);
	} else if (Getattr(n, "feature:callback")) {
	  String *ds = NewStringf("swig_ptr: %s", Getattr(n, "feature:callback:name"));
	  Printf(builtin_methods, "  { \"%s\", (PyCFunction)(void(*)(void))%s, %s, \"%s\" },\n", symname, wname, pyflags, ds);
	  Delete(ds);
	  have_builtin_static_member_method_callback = true;
	} else {
	  Printf(builtin_methods, "  { \"%s\", (PyCFunction)(void(*)(void))%s, %s, \"\" },\n", symname, wname, pyflags);
	}
	Delete(fullname);
	Delete(wname);
	Delete(pyflags);
      }
      return SWIG_OK;
    }

    if (Getattr(n, "sym:nextSibling")) {
      return SWIG_OK;
    }

    if (shadow) {
      String *staticfunc_name = NewString(fastproxy ? "_swig_new_static_method" : "staticmethod");
      bool fast = (fastproxy && !have_addtofunc(n)) || Getattr(n, "feature:callback");
      if (!fast || olddefs) {
	String *parms = make_pyParmList(n, false, false, kw);
	String *callParms = make_pyParmList(n, false, true, kw);
	Printv(f_shadow, "\n", tab4, "@staticmethod", NIL);
	Printv(f_shadow, "\n", tab4, "def ", symname, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
	if (Node *node_with_doc = find_overload_with_docstring(n))
	  Printv(f_shadow, tab8, docstring(node_with_doc, AUTODOC_STATICFUNC, tab8), "\n", NIL);
	if (have_pythonprepend(n))
	  Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
	if (have_pythonappend(n)) {
	  Printv(f_shadow, tab8, "val = ", funcCall(Swig_name_member(NSPACE_TODO, class_name, symname), callParms), "\n", NIL);
	  Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
	  Printv(f_shadow, tab8, "return val\n", NIL);
	} else {
	  Printv(f_shadow, tab8, "return ", funcCall(Swig_name_member(NSPACE_TODO, class_name, symname), callParms), "\n", NIL);
	}
      }

      // Below may result in a 2nd definition of the method when -olddefs is used. The Python interpreter will use the second definition as it overwrites the first.
      if (fast) {
	Printv(f_shadow, tab4, symname, " = ", staticfunc_name, "(", module, ".", Swig_name_member(NSPACE_TODO, class_name, symname),
	       ")\n", NIL);
      }
      Delete(staticfunc_name);
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * constructorDeclaration()
   * ------------------------------------------------------------ */

  virtual int constructorHandler(Node *n) {
    String *symname = Getattr(n, "sym:name");
    int oldshadow = shadow;
    int use_director = Swig_directorclass(n);

    /*
     * If we're wrapping the constructor of a C++ director class, prepend a new parameter
     * to receive the scripting language object (e.g. 'self')
     *
     */
    Swig_save("python:constructorHandler", n, "parms", NIL);
    if (use_director) {
      Parm *parms = Getattr(n, "parms");
      Parm *self;
      String *name = NewString("self");
      String *type = NewString("PyObject");
      SwigType_add_pointer(type);
      self = NewParm(type, name, n);
      Delete(type);
      Delete(name);
      Setattr(self, "lname", "O");
      if (parms)
	set_nextSibling(self, parms);
      Setattr(n, "parms", self);
      Setattr(n, "wrap:self", "1");
      Setattr(n, "hidden", "1");
      Delete(self);
    }

    if (shadow)
      shadow = shadow | PYSHADOW_MEMBER;
    Language::constructorHandler(n);
    shadow = oldshadow;

    Delattr(n, "wrap:self");
    Swig_restore(n);

    if (!Getattr(n, "sym:nextSibling")) {
      if (shadow) {
	int allow_kwargs = (check_kwargs(n) && (!Getattr(n, "sym:overloaded"))) ? 1 : 0;
	int handled_as_init = 0;
	if (!have_constructor) {
	  String *nname = Getattr(n, "sym:name");
	  String *sname = Getattr(getCurrentClass(), "sym:name");
	  String *cname = Swig_name_construct(NSPACE_TODO, sname);
	  handled_as_init = (Strcmp(nname, sname) == 0) || (Strcmp(nname, cname) == 0);
	  Delete(cname);
	}

	String *subfunc = Swig_name_construct(NSPACE_TODO, symname);
	if (!have_constructor && handled_as_init) {
	  if (!builtin) {
	    if (Getattr(n, "feature:shadow")) {
	      String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n), "%feature(\"shadow\")");
	      String *pyaction = NewStringf("%s.%s", module, subfunc);
	      Replaceall(pycode, "$action", pyaction);
	      Delete(pyaction);
	      Printv(f_shadow, pycode, "\n", NIL);
	      Delete(pycode);
	    } else {
	      String *pass_self = NewString("");
	      Node *parent = Swig_methodclass(n);
	      String *classname = Swig_class_name(parent);
	      String *rclassname = Swig_class_name(getCurrentClass());
	      assert(rclassname);
	      (void)rclassname;

	      String *parms = make_pyParmList(n, true, false, allow_kwargs);
	      /* Pass 'self' only if using director */
	      String *callParms = make_pyParmList(n, false, true, allow_kwargs, true);

	      if (use_director) {
		Insert(callParms, 0, "_self, ");
		Printv(pass_self, tab8, NIL);
		Printf(pass_self, "if self.__class__ == %s:\n", classname);
		//Printv(pass_self, tab8, tab4, "args = (None,) + args\n", tab8, "else:\n", tab8, tab4, "args = (self,) + args\n", NIL);
		Printv(pass_self, tab8, tab4, "_self = None\n", tab8, "else:\n", tab8, tab4, "_self = self\n", NIL);
	      }

	      Printv(f_shadow, "\n", tab4, "def __init__(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
	      if (Node *node_with_doc = find_overload_with_docstring(n))
		Printv(f_shadow, tab8, docstring(node_with_doc, AUTODOC_CTOR, tab8), "\n", NIL);
	      if (have_pythonprepend(n))
		Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
	      Printv(f_shadow, pass_self, NIL);
	      Printv(f_shadow, tab8, module, ".", class_name, "_swiginit(self, ", funcCall(subfunc, callParms), ")\n", NIL);
	      if (have_pythonappend(n))
		Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n\n", NIL);
	      Delete(pass_self);
	    }
	    have_constructor = 1;
	  }
	} else {
	  /* Hmmm. We seem to be creating a different constructor.  We're just going to create a
	     function for it. */
	  if (!builtin) {
	    if (Getattr(n, "feature:shadow")) {
	      String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), "", Getfile(n), Getline(n), "%feature(\"shadow\")");
	      String *pyaction = NewStringf("%s.%s", module, subfunc);
	      Replaceall(pycode, "$action", pyaction);
	      Delete(pyaction);
	      Printv(f_shadow_stubs, pycode, "\n", NIL);
	      Delete(pycode);
	    } else {
	      String *parms = make_pyParmList(n, false, false, allow_kwargs);
	      String *callParms = make_pyParmList(n, false, true, allow_kwargs);

	      Printv(f_shadow_stubs, "\ndef ", symname, "(", parms, ")", returnTypeAnnotation(n), ":\n", NIL);
	      if (Node *node_with_doc = find_overload_with_docstring(n))
		Printv(f_shadow_stubs, tab4, docstring(node_with_doc, AUTODOC_CTOR, tab4), "\n", NIL);
	      if (have_pythonprepend(n))
		Printv(f_shadow_stubs, indent_pythoncode(pythonprepend(n), tab4, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
	      Printv(f_shadow_stubs, tab4, "val = ", funcCall(subfunc, callParms), "\n", NIL);
	      if (have_pythonappend(n))
		Printv(f_shadow_stubs, indent_pythoncode(pythonappend(n), tab4, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
	      Printv(f_shadow_stubs, tab4, "return val\n", NIL);
	    }
	  } else {
	    Printf(f_shadow_stubs, "%s = %s\n", symname, subfunc);
	  }
	}
	Delete(subfunc);
      }
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * destructorHandler()
   * ------------------------------------------------------------ */

  virtual int destructorHandler(Node *n) {
    String *symname = Getattr(n, "sym:name");
    int oldshadow = shadow;

    if (builtin && in_class) {
      Node *cls = Swig_methodclass(n);
      // Use the destructor for the tp_dealloc slot unless a user overrides it with another method
      if (!Getattr(cls, "feature:python:tp_dealloc")) {
	Setattr(n, "feature:python:slot", "tp_dealloc");
	Setattr(n, "feature:python:slot:functype", "destructor");
      }
    }

    if (shadow)
      shadow = shadow | PYSHADOW_MEMBER;
    //Setattr(n,"emit:dealloc","1");
    Language::destructorHandler(n);
    shadow = oldshadow;

    if (shadow) {
      if (Getattr(n, "feature:shadow")) {
	String *pycode = indent_pythoncode(Getattr(n, "feature:shadow"), tab4, Getfile(n), Getline(n), "%feature(\"shadow\")");
	String *pyaction = NewStringf("%s.%s", module, Swig_name_destroy(NSPACE_TODO, symname));
	Replaceall(pycode, "$action", pyaction);
	Delete(pyaction);
	Printv(f_shadow, pycode, "\n", NIL);
	Delete(pycode);
      } else {
	Printv(f_shadow, tab4, "__swig_destroy__ = ", module, ".", Swig_name_destroy(NSPACE_TODO, symname), "\n", NIL);
	if (!have_pythonprepend(n) && !have_pythonappend(n)) {
	  return SWIG_OK;
	}
	Printv(f_shadow, tab4, "def __del__(self):\n", NIL);
	if (have_docstring(n))
	  Printv(f_shadow, tab8, docstring(n, AUTODOC_DTOR, tab8), "\n", NIL);
	if (have_pythonprepend(n))
	  Printv(f_shadow, indent_pythoncode(pythonprepend(n), tab8, Getfile(n), Getline(n), "%pythonprepend or %feature(\"pythonprepend\")"), "\n", NIL);
	if (have_pythonappend(n))
	  Printv(f_shadow, indent_pythoncode(pythonappend(n), tab8, Getfile(n), Getline(n), "%pythonappend or %feature(\"pythonappend\")"), "\n", NIL);
	Printv(f_shadow, tab8, "pass\n", NIL);
	Printv(f_shadow, "\n", NIL);
      }
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * membervariableHandler()
   * ------------------------------------------------------------ */

  virtual int membervariableHandler(Node *n) {
    String *symname = Getattr(n, "sym:name");

    int oldshadow = shadow;
    if (shadow)
      shadow = shadow | PYSHADOW_MEMBER;
    Language::membervariableHandler(n);
    shadow = oldshadow;

    if (shadow && !builtin) {
      String *mname = Swig_name_member(NSPACE_TODO, class_name, symname);
      String *setname = Swig_name_set(NSPACE_TODO, mname);
      String *getname = Swig_name_get(NSPACE_TODO, mname);
      int assignable = !is_immutable(n);
      String *variable_annotation = variableAnnotation(n);
      Printv(f_shadow, tab4, symname, variable_annotation, " = property(", module, ".", getname, NIL);
      if (assignable)
	Printv(f_shadow, ", ", module, ".", setname, NIL);
      if (have_docstring(n)) {
	String *s = docstring(n, AUTODOC_VAR, tab4);
	if (Len(s))
	  Printv(f_shadow, ", doc=", s, NIL);
      }
      Printv(f_shadow, ")\n", NIL);
      Delete(variable_annotation);
      Delete(mname);
      Delete(setname);
      Delete(getname);
    }

    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * staticmembervariableHandler()
   * ------------------------------------------------------------ */

  virtual int staticmembervariableHandler(Node *n) {
    Swig_save("builtin_staticmembervariableHandler", n, "builtin_symname", NIL);
    Language::staticmembervariableHandler(n);
    Swig_restore(n);

    if (GetFlag(n, "wrappedasconstant"))
      return SWIG_OK;

    String *symname = Getattr(n, "sym:name");

    if (shadow) {
      if (!builtin && GetFlag(n, "hasconsttype")) {
	String *mname = Swig_name_member(NSPACE_TODO, class_name, symname);
	Printf(f_shadow_stubs, "%s.%s = %s.%s.%s\n", class_name, symname, module, global_name, mname);
	Delete(mname);
      } else {
	String *mname = Swig_name_member(NSPACE_TODO, class_name, symname);
	String *getname = Swig_name_get(NSPACE_TODO, mname);
	String *wrapgetname = Swig_name_wrapper(getname);
	String *vargetname = NewStringf("Swig_var_%s", getname);
	String *setname = Swig_name_set(NSPACE_TODO, mname);
	String *wrapsetname = Swig_name_wrapper(setname);
	String *varsetname = NewStringf("Swig_var_%s", setname);

	Wrapper *f = NewWrapper();
	Printv(f->def, "SWIGINTERN PyObject *", wrapgetname, "(PyObject *SWIGUNUSEDPARM(self), PyObject *SWIGUNUSEDPARM(args)) {", NIL);
	Printv(f->code, "  return ", vargetname, "();\n", NIL);
	Append(f->code, "}\n");
	add_method(getname, wrapgetname, 0);
	Wrapper_print(f, f_wrappers);
	DelWrapper(f);
	int assignable = !is_immutable(n);
	if (assignable) {
	  int funpack = fastunpack;
	  Wrapper *f = NewWrapper();
	  Printv(f->def, "SWIGINTERN PyObject *", wrapsetname, "(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {", NIL);
	  Wrapper_add_local(f, "res", "int res");
	  if (!funpack) {
	    Wrapper_add_local(f, "value", "PyObject *value");
	    Append(f->code, "if (!PyArg_ParseTuple(args, \"O:set\", &value)) return NULL;\n");
	  }
	  Printf(f->code, "res = %s(%s);\n", varsetname, funpack ? "args" : "value");
	  Append(f->code, "return !res ? SWIG_Py_Void() : NULL;\n");
	  Append(f->code, "}\n");
	  Wrapper_print(f, f_wrappers);
	  add_method(setname, wrapsetname, 0, 0, funpack, 1, 1);
	  DelWrapper(f);
	}
	if (!builtin) {
	  Printv(f_shadow, tab4, symname, " = property(", module, ".", getname, NIL);
	  if (assignable)
	    Printv(f_shadow, ", ", module, ".", setname, NIL);
	  if (have_docstring(n)) {
	    String *s = docstring(n, AUTODOC_VAR, tab4);
	    if (Len(s))
	      Printv(f_shadow, ", doc=", s, NIL);
	  }
	  Printv(f_shadow, ")\n", NIL);
	}
	String *getter = Getattr(n, "pybuiltin:getter");
	String *setter = Getattr(n, "pybuiltin:setter");
	Hash *h = NULL;
	if (getter || setter) {
	  h = Getattr(builtin_getset, symname);
	  if (!h) {
	    h = NewHash();
	    Setattr(h, "static", "1");
	    Setattr(builtin_getset, symname, h);
	  }
	}
	if (getter)
	  Setattr(h, "getter", getter);
	if (setter)
	  Setattr(h, "setter", setter);
	if (h)
	  Delete(h);
	Delete(mname);
	Delete(getname);
	Delete(wrapgetname);
	Delete(vargetname);
	Delete(setname);
	Delete(wrapsetname);
	Delete(varsetname);
      }
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * memberconstantHandler()
   * ------------------------------------------------------------ */

  virtual int memberconstantHandler(Node *n) {
    String *symname = Getattr(n, "sym:name");
    if (builtin && in_class) {
      Swig_save("builtin_memberconstantHandler", n, "pybuiltin:symname", NIL);
      Setattr(n, "pybuiltin:symname", symname);
    }
    int oldshadow = shadow;
    if (shadow)
      shadow = shadow | PYSHADOW_MEMBER;
    Language::memberconstantHandler(n);
    shadow = oldshadow;

    if (builtin && in_class) {
      Swig_restore(n);
    } else if (shadow) {
      Printv(f_shadow, tab4, symname, " = ", module, ".", Swig_name_member(NSPACE_TODO, class_name, symname), "\n", NIL);
      if (have_docstring(n))
	Printv(f_shadow, tab4, docstring(n, AUTODOC_CONST, tab4), "\n", NIL);
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * insertDirective()
   *
   * Hook for %insert directive.   We're going to look for special %shadow inserts
   * as a special case so we can do indenting correctly
   * ------------------------------------------------------------ */

  virtual int insertDirective(Node *n) {
    String *code = Getattr(n, "code");
    String *section = Getattr(n, "section");

    if (!ImportMode && (Cmp(section, "python") == 0 || Cmp(section, "shadow") == 0)) {
      if (shadow) {
	String *pycode = indent_pythoncode(code, shadow_indent, Getfile(n), Getline(n), "%pythoncode or %insert(\"python\") block");
	Printv(f_shadow, pycode, NIL);
	Delete(pycode);
      }
    } else if (!ImportMode && (Cmp(section, "pythonbegin") == 0)) {
      if (shadow) {
	String *pycode = indent_pythoncode(code, "", Getfile(n), Getline(n), "%pythonbegin or %insert(\"pythonbegin\") block");
	Printv(f_shadow_begin, pycode, NIL);
	Delete(pycode);
      }
    } else {
      Language::insertDirective(n);
    }
    return SWIG_OK;
  }

  virtual String *runtimeCode() {
    String *s = NewString("");
    String *shead = Swig_include_sys("pyhead.swg");
    if (!shead) {
      Printf(stderr, "*** Unable to open 'pyhead.swg'\n");
    } else {
      Append(s, shead);
      Delete(shead);
    }
    String *serrors = Swig_include_sys("pyerrors.swg");
    if (!serrors) {
      Printf(stderr, "*** Unable to open 'pyerrors.swg'\n");
    } else {
      Append(s, serrors);
      Delete(serrors);
    }
    String *sthread = Swig_include_sys("pythreads.swg");
    if (!sthread) {
      Printf(stderr, "*** Unable to open 'pythreads.swg'\n");
    } else {
      Append(s, sthread);
      Delete(sthread);
    }
    String *sapi = Swig_include_sys("pyapi.swg");
    if (!sapi) {
      Printf(stderr, "*** Unable to open 'pyapi.swg'\n");
    } else {
      Append(s, sapi);
      Delete(sapi);
    }
    String *srun = Swig_include_sys("pyrun.swg");
    if (!srun) {
      Printf(stderr, "*** Unable to open 'pyrun.swg'\n");
    } else {
      Append(s, srun);
      Delete(srun);
    }
    return s;
  }

  virtual String *defaultExternalRuntimeFilename() {
    return NewString("swigpyrun.h");
  }

  /*----------------------------------------------------------------------
   * kwargsSupport()
   *--------------------------------------------------------------------*/

  bool kwargsSupport() const {
    return true;
  }
};

/* ---------------------------------------------------------------
 * classDirectorMethod()
 *
 * Emit a virtual director method to pass a method call on to the
 * underlying Python object.
 *
 * ** Moved it here due to internal error on gcc-2.96 **
 * --------------------------------------------------------------- */
int PYTHON::classDirectorMethods(Node *n) {
  director_method_index = 0;
  return Language::classDirectorMethods(n);
}


int PYTHON::classDirectorMethod(Node *n, Node *parent, String *super) {
  int is_void = 0;
  int is_pointer = 0;
  String *decl = Getattr(n, "decl");
  String *name = Getattr(n, "name");
  String *classname = Getattr(parent, "sym:name");
  String *c_classname = Getattr(parent, "name");
  String *symname = Getattr(n, "sym:name");
  String *declaration = NewString("");
  ParmList *l = Getattr(n, "parms");
  Wrapper *w = NewWrapper();
  String *tm;
  String *wrap_args = NewString("");
  SwigType *returntype = Getattr(n, "type");
  String *value = Getattr(n, "value");
  String *storage = Getattr(n, "storage");
  bool pure_virtual = false;
  int status = SWIG_OK;
  int idx;
  bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;

  if (builtin) {
    // Rename any wrapped parameters called 'self' as the generated code contains a variable with same name
    Parm *p;
    for (p = l; p; p = nextSibling(p)) {
      String *arg = Getattr(p, "name");
      if (arg && Cmp(arg, "self") == 0)
	Delattr(p, "name");
    }
  }

  if (Cmp(storage, "virtual") == 0) {
    if (Cmp(value, "0") == 0) {
      pure_virtual = true;
    }
  }

  /* determine if the method returns a pointer */
  is_pointer = SwigType_ispointer_return(decl);
  is_void = (!Cmp(returntype, "void") && !is_pointer);

  /* virtual method definition */
  String *target;
  String *pclassname = NewStringf("SwigDirector_%s", classname);
  String *qualified_name = NewStringf("%s::%s", pclassname, name);
  SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
  target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
  Printf(w->def, "%s", target);
  Delete(qualified_name);
  Delete(target);
  /* header declaration */
  target = Swig_method_decl(rtype, decl, name, l, 1);
  Printf(declaration, "    virtual %s", target);
  Delete(target);

  // Get any exception classes in the throws typemap
  if (Getattr(n, "noexcept")) {
    Append(w->def, " noexcept");
    Append(declaration, " noexcept");
  }
  ParmList *throw_parm_list = 0;
  if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
    Parm *p;
    int gencomma = 0;

    Append(w->def, " throw(");
    Append(declaration, " throw(");

    if (throw_parm_list)
      Swig_typemap_attach_parms("throws", throw_parm_list, 0);
    for (p = throw_parm_list; p; p = nextSibling(p)) {
      if (Getattr(p, "tmap:throws")) {
	if (gencomma++) {
	  Append(w->def, ", ");
	  Append(declaration, ", ");
	}
	String *str = SwigType_str(Getattr(p, "type"), 0);
	Append(w->def, str);
	Append(declaration, str);
	Delete(str);
      }
    }

    Append(w->def, ")");
    Append(declaration, ")");
  }

  Append(w->def, " {");
  Append(declaration, ";\n");

  /* declare method return value
   * if the return value is a reference or const reference, a specialized typemap must
   * handle it, including declaration of c_result ($result).
   */
  if (!is_void && (!ignored_method || pure_virtual)) {
    if (!SwigType_isclass(returntype)) {
      if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
	String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
	Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
	Delete(construct_result);
      } else {
	Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
      }
    } else {
      String *cres = SwigType_lstr(returntype, "c_result");
      Printf(w->code, "%s;\n", cres);
      Delete(cres);
    }
  }

  if (builtin) {
    Printv(w->code, "PyObject *self = NULL;\n", NIL);
    Printv(w->code, "(void)self;\n", NIL);
  }

  if (ignored_method) {
    if (!pure_virtual) {
      if (!is_void)
	Printf(w->code, "return ");
      String *super_call = Swig_method_call(super, l);
      Printf(w->code, "%s;\n", super_call);
      Delete(super_call);
    } else {
      Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"Attempted to invoke pure virtual method %s::%s\");\n", SwigType_namestr(c_classname),
	     SwigType_namestr(name));
    }
  } else {
    /* attach typemaps to arguments (C/C++ -> Python) */
    String *arglist = NewString("");
    String *parse_args = NewString("");

    Swig_director_parms_fixup(l);

    /* remove the wrapper 'w' since it was producing spurious temps */
    Swig_typemap_attach_parms("in", l, 0);
    Swig_typemap_attach_parms("directorin", l, w);
    Swig_typemap_attach_parms("directorargout", l, w);

    Parm *p;
    char source[256];

    int outputs = 0;
    if (!is_void)
      outputs++;

    /* build argument list and type conversion string */
    idx = 0;
    p = l;
    int use_parse = 0;
    while (p) {
      if (checkAttribute(p, "tmap:in:numinputs", "0")) {
	p = Getattr(p, "tmap:in:next");
	continue;
      }

      if (Getattr(p, "tmap:directorargout") != 0)
	outputs++;

      String *pname = Getattr(p, "name");
      String *ptype = Getattr(p, "type");

      Putc(',', arglist);
      if ((tm = Getattr(p, "tmap:directorin")) != 0) {
	String *parse = Getattr(p, "tmap:directorin:parse");
	if (!parse) {
	  sprintf(source, "obj%d", idx++);
	  String *input = NewString(source);
	  Setattr(p, "emit:directorinput", input);
	  Replaceall(tm, "$input", input);
	  Delete(input);
	  Replaceall(tm, "$owner", "0");
	  /* Wrapper_add_localv(w, source, "swig::SwigVar_PyObject", source, "= 0", NIL); */
	  Printv(wrap_args, "swig::SwigVar_PyObject ", source, ";\n", NIL);

	  Printv(wrap_args, tm, "\n", NIL);
	  Printv(arglist, "(PyObject *)", source, NIL);
	  Putc('O', parse_args);
	} else {
	  use_parse = 1;
	  Append(parse_args, parse);
	  Setattr(p, "emit:directorinput", pname);
	  Replaceall(tm, "$input", pname);
	  Replaceall(tm, "$owner", "0");
	  if (Len(tm) == 0)
	    Append(tm, pname);
	  Append(arglist, tm);
	}
	p = Getattr(p, "tmap:directorin:next");
	continue;
      } else if (Cmp(ptype, "void")) {
	/* special handling for pointers to other C++ director classes.
	 * ideally this would be left to a typemap, but there is currently no
	 * way to selectively apply the dynamic_cast<> to classes that have
	 * directors.  in other words, the type "SwigDirector_$1_lname" only exists
	 * for classes with directors.  we avoid the problem here by checking
	 * module.wrap::directormap, but it's not clear how to get a typemap to
	 * do something similar.  perhaps a new default typemap (in addition
	 * to SWIGTYPE) called DIRECTORTYPE?
	 */
	if (SwigType_ispointer(ptype) || SwigType_isreference(ptype)) {
	  Node *module = Getattr(parent, "module");
	  Node *target = Swig_directormap(module, ptype);
	  sprintf(source, "obj%d", idx++);
	  String *nonconst = 0;
	  /* strip pointer/reference --- should move to Swig/stype.c */
	  String *nptype = NewString(Char(ptype) + 2);
	  /* name as pointer */
	  String *ppname = Copy(pname);
	  if (SwigType_isreference(ptype)) {
	    Insert(ppname, 0, "&");
	  }
	  /* if necessary, cast away const since Python doesn't support it! */
	  if (SwigType_isconst(nptype)) {
	    nonconst = NewStringf("nc_tmp_%s", pname);
	    String *nonconst_i = NewStringf("= const_cast< %s >(%s)", SwigType_lstr(ptype, 0), ppname);
	    Wrapper_add_localv(w, nonconst, SwigType_lstr(ptype, 0), nonconst, nonconst_i, NIL);
	    Delete(nonconst_i);
	    Swig_warning(WARN_LANG_DISCARD_CONST, input_file, line_number,
			 "Target language argument '%s' discards const in director method %s::%s.\n",
			 SwigType_str(ptype, pname), SwigType_namestr(c_classname), SwigType_namestr(name));
	  } else {
	    nonconst = Copy(ppname);
	  }
	  Delete(nptype);
	  Delete(ppname);
	  String *mangle = SwigType_manglestr(ptype);
	  if (target) {
	    String *director = NewStringf("director_%s", mangle);
	    Wrapper_add_localv(w, director, "Swig::Director *", director, "= 0", NIL);
	    Wrapper_add_localv(w, source, "swig::SwigVar_PyObject", source, "= 0", NIL);
	    Printf(wrap_args, "%s = SWIG_DIRECTOR_CAST(%s);\n", director, nonconst);
	    Printf(wrap_args, "if (!%s) {\n", director);
	    Printf(wrap_args, "%s = SWIG_InternalNewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
	    Append(wrap_args, "} else {\n");
	    Printf(wrap_args, "%s = %s->swig_get_self();\n", source, director);
	    Printf(wrap_args, "SWIG_Py_INCREF((PyObject *)%s);\n", source);
	    Append(wrap_args, "}\n");
	    Delete(director);
	    Printv(arglist, source, NIL);
	  } else {
	    Wrapper_add_localv(w, source, "swig::SwigVar_PyObject", source, "= 0", NIL);
	    Printf(wrap_args, "%s = SWIG_InternalNewPointerObj(%s, SWIGTYPE%s, 0);\n", source, nonconst, mangle);
	    //Printf(wrap_args, "%s = SWIG_NewPointerObj(%s, SWIGTYPE_p_%s, 0);\n",
	    //       source, nonconst, base);
	    Printv(arglist, source, NIL);
	  }
	  Putc('O', parse_args);
	  Delete(mangle);
	  Delete(nonconst);
	} else {
	  Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file, line_number,
		       "Unable to use type %s as a function argument in director method %s::%s (skipping method).\n", SwigType_str(ptype, 0),
		       SwigType_namestr(c_classname), SwigType_namestr(name));
	  status = SWIG_NOWRAP;
	  break;
	}
      }
      p = nextSibling(p);
    }

    /* add the method name as a PyString */
    String *pyname = Getattr(n, "sym:name");

    int allow_thread = threads_enable(n);

    if (allow_thread) {
      thread_begin_block(n, w->code);
      Append(w->code, "{\n");
    }

    /* wrap complex arguments to PyObjects */
    Printv(w->code, wrap_args, NIL);

    /* pass the method call on to the Python object */
    if (dirprot_mode() && !is_public(n)) {
      Printf(w->code, "swig_set_inner(\"%s\", true);\n", name);
    }


    Append(w->code, "if (!swig_get_self()) {\n");
    Printf(w->code, "  Swig::DirectorException::raise(\"'self' uninitialized, maybe you forgot to call %s.__init__.\");\n", classname);
    Append(w->code, "}\n");
    Append(w->code, "#if defined(SWIG_PYTHON_DIRECTOR_VTABLE)\n");
    Printf(w->code, "const size_t swig_method_index = %d;\n", director_method_index++);
    Printf(w->code, "const char *const swig_method_name = \"%s\";\n", pyname);

    Append(w->code, "PyObject *method = swig_get_method(swig_method_index, swig_method_name);\n");
    if (Len(parse_args) > 0) {
      if (use_parse) {
	Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallFunction(method, (char *)\"(%s)\" %s);\n", Swig_cresult_name(), parse_args, arglist);
      } else {
	Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallFunctionObjArgs(method %s, NULL);\n", Swig_cresult_name(), arglist);
      }
    } else {
      Append(w->code, "swig::SwigVar_PyObject args = PyTuple_New(0);\n");
      Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_Call(method, (PyObject *) args, NULL);\n", Swig_cresult_name());
    }
    Append(w->code, "#else\n");
    if (Len(parse_args) > 0) {
      if (use_parse) {
	Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallMethod(swig_get_self(), (char *)\"%s\", (char *)\"(%s)\" %s);\n", Swig_cresult_name(), pyname, parse_args, arglist);
      } else {
	Printf(w->code, "swig::SwigVar_PyObject swig_method_name = SWIG_Python_str_FromChar(\"%s\");\n", pyname);
	Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallMethodObjArgs(swig_get_self(), (PyObject *) swig_method_name %s, NULL);\n", Swig_cresult_name(), arglist);
      }
    } else {
      Printf(w->code, "swig::SwigVar_PyObject swig_method_name = SWIG_Python_str_FromChar(\"%s\");\n", pyname);
      Printf(w->code, "swig::SwigVar_PyObject %s = PyObject_CallMethodObjArgs(swig_get_self(), (PyObject *) swig_method_name, NULL);\n", Swig_cresult_name());
    }
    Append(w->code, "#endif\n");

    if (dirprot_mode() && !is_public(n))
      Printf(w->code, "swig_set_inner(\"%s\", false);\n", name);

    /* exception handling */
    tm = Swig_typemap_lookup("director:except", n, Swig_cresult_name(), 0);
    if (!tm) {
      tm = Getattr(n, "feature:director:except");
      if (tm)
	tm = Copy(tm);
    }
    Printf(w->code, "if (!%s) {\n", Swig_cresult_name());
    Append(w->code, "  PyObject *error = PyErr_Occurred();\n");
    if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
      Replaceall(tm, "$error", "error");
      Printv(w->code, Str(tm), "\n", NIL);
    } else {
      Append(w->code, "  if (error) {\n");
      Printf(w->code, "    Swig::DirectorMethodException::raise(\"Error detected when calling '%s.%s'\");\n", classname, pyname);
      Append(w->code, "  }\n");
    }
    Append(w->code, "}\n");
    Delete(tm);

    /*
     * Python method may return a simple object, or a tuple.
     * for in/out arguments, we have to extract the appropriate PyObjects from the tuple,
     * then marshal everything back to C/C++ (return value and output arguments).
     *
     */

    /* marshal return value and other outputs (if any) from PyObject to C/C++ type */

    String *cleanup = NewString("");
    String *outarg = NewString("");

    if (outputs > 1) {
      Wrapper_add_local(w, "output", "PyObject *output");
      Printf(w->code, "if (!PyTuple_Check(%s)) {\n", Swig_cresult_name());
      Printf(w->code, "  Swig::DirectorTypeMismatchException::raise(\"Python method %s.%sfailed to return a tuple.\");\n", classname, pyname);
      Append(w->code, "}\n");
    }

    idx = 0;

    /* marshal return value */
    if (!is_void) {
      tm = Swig_typemap_lookup("directorout", n, Swig_cresult_name(), w);
      if (tm != 0) {
	if (outputs > 1) {
	  Printf(w->code, "output = PyTuple_GetItem(%s, %d);\n", Swig_cresult_name(), idx++);
	  Replaceall(tm, "$input", "output");
	} else {
	  Replaceall(tm, "$input", Swig_cresult_name());
	}
	char temp[24];
	sprintf(temp, "%d", idx);
	Replaceall(tm, "$argnum", temp);

	/* TODO check this */
	if (Getattr(n, "wrap:disown")) {
	  Replaceall(tm, "$disown", "SWIG_POINTER_DISOWN");
	} else {
	  Replaceall(tm, "$disown", "0");
	}
	if (Getattr(n, "tmap:directorout:implicitconv")) {
	  Replaceall(tm, "$implicitconv", get_implicitconv_flag(n));
	}
	Replaceall(tm, "$result", "c_result");
	Printv(w->code, tm, "\n", NIL);
	Delete(tm);
      } else {
	Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
		     "Unable to use return type %s in director method %s::%s (skipping method).\n", SwigType_str(returntype, 0), SwigType_namestr(c_classname),
		     SwigType_namestr(name));
	status = SWIG_ERROR;
      }
    }

    /* marshal outputs */
    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:directorargout")) != 0) {
	if (outputs > 1) {
	  Printf(w->code, "output = PyTuple_GetItem(%s, %d);\n", Swig_cresult_name(), idx++);
	  Replaceall(tm, "$result", "output");
	} else {
	  Replaceall(tm, "$result", Swig_cresult_name());
	}
	Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
	Printv(w->code, tm, "\n", NIL);
	p = Getattr(p, "tmap:directorargout:next");
      } else {
	p = nextSibling(p);
      }
    }

    /* any existing helper functions to handle this? */
    if (allow_thread) {
      Append(w->code, "}\n");
      thread_end_block(n, w->code);
    }

    Delete(parse_args);
    Delete(arglist);
    Delete(cleanup);
    Delete(outarg);
  }

  if (!is_void) {
    if (!(ignored_method && !pure_virtual)) {
      String *rettype = SwigType_str(returntype, 0);
      if (!SwigType_isreference(returntype)) {
	Printf(w->code, "return (%s) c_result;\n", rettype);
      } else {
	Printf(w->code, "return (%s) *c_result;\n", rettype);
      }
      Delete(rettype);
    }
  }

  Append(w->code, "}\n");

  // We expose protected methods via an extra public inline method which makes a straight call to the wrapped class' method
  String *inline_extra_method = NewString("");
  if (dirprot_mode() && !is_public(n) && !pure_virtual) {
    Printv(inline_extra_method, declaration, NIL);
    String *extra_method_name = NewStringf("%sSwigPublic", name);
    Replaceall(inline_extra_method, name, extra_method_name);
    Replaceall(inline_extra_method, ";\n", " {\n      ");
    if (!is_void)
      Printf(inline_extra_method, "return ");
    String *methodcall = Swig_method_call(super, l);
    Printv(inline_extra_method, methodcall, ";\n    }\n", NIL);
    Delete(methodcall);
    Delete(extra_method_name);
  }

  /* emit the director method */
  if (status == SWIG_OK) {
    Replaceall(w->code, "$isvoid", is_void ? "1" : "0");
    if (!Getattr(n, "defaultargs")) {
      Replaceall(w->code, "$symname", symname);
      Wrapper_print(w, f_directors);
      Printv(f_directors_h, declaration, NIL);
      Printv(f_directors_h, inline_extra_method, NIL);
    }
  }

  /* clean up */
  Delete(wrap_args);
  Delete(pclassname);
  DelWrapper(w);
  return status;
}

/* -----------------------------------------------------------------------------
 * swig_python()    - Instantiate module
 * ----------------------------------------------------------------------------- */

static Language *new_swig_python() {
  return new PYTHON();
}
extern "C" Language *swig_python(void) {
  return new_swig_python();
}
