def main(): #fn = sys.argv[1] fn = settings.files[0] app = creator_app() app.process_include_files() classes = {} temp_cpp_ss = [] temp_cpp_ss.append('#define SWIG') temp_cpp_ss.append('#define CREATE_VCW') for s in settings.defines: temp_cpp_ss.append('#define %s'%s) for s in app.c_define_lines: temp_cpp_ss.append(s) for fn in settings.files: # Create private copies with culled headers #tfn = parse_cpp_header.get_temp_filename('h') temp_cpp_ss.append(app.load_file(fn)) #temp_cpp_ss.append('#include <%s>'%fn) temp_cpp_ss.append('') temp_cpp = '\n'.join(temp_cpp_ss) temp_cpp = temp_cpp.replace('#include','//#include') classes = cpp_header_parser.parse_header(temp_cpp, settings.include_paths, {}) if classes is None: return # Distribute and check class configs for cls in classes.itervalues(): cls_config = settings.class_config.get(cls.name, {}) cls.cls_config = cls_config for k in cls_config.keys(): if not k in ALLOWED_CLS_CONFIG_KEYS: raise AssertionError("'%s' is not supported class_config key"%k) # Update derived classes configs for cls in classes.itervalues(): derived_classes = cls.find_derived_classes(classes) for ocls in derived_classes: if not ocls.cls_config: ocls.cls_config = cls.cls_config # Add 'pyvirtual' flag to functions for which we should create wrapper for cls in classes.itervalues(): cls_config = cls.cls_config for func in cls.func_list: if 'virtual' in func.flags: excluded = False for fn in cls_config.get('excluded_methods', []): if func.name == fn: excluded = True break if not excluded: func.flags.add('pyvirtual') # Also add new member, - a list of virtual # functions that are added (exclusively) in # add_wrapper_code-section below cls.additional_virtual_functions = [] # # Parse 'add_wrapper_code' section add_wrapper_code = getattr(settings, 'add_wrapper_code', None) add_wrapper_code_classes = {} if add_wrapper_code: add_wrapper_code_classes = cpp_header_parser.parse_header(add_wrapper_code, settings.include_paths, {}) if add_wrapper_code_classes is None: return # # Add function with 'pyvirtual' flag to main classes dictionary straight away for class_name, awc_cls in add_wrapper_code_classes.iteritems(): try: cls = classes[class_name] except KeyError: raise AssertionError("Classes declared in add_wrapper_code " "section must exist in included header " "files. Class '%s' did not exist." % \ (class_name)) derived_classes = cls.find_derived_classes(classes) for func in awc_cls.func_list: if 'pyvirtual' in func.flags: # Must create wrapper for this cls.additional_virtual_functions.append(func) for dcls in derived_classes: dcls.additional_virtual_functions.append(func) v_py_funcnames = set() class_codes = [] funcsigmap = {} dbg_func = 'MySWIGOutputDebugString' # Add this string to func names when stored into object func_appendum = '_t_' pfunc_addendum = '_Parent' #if settings.logging: #else: swig_ignores = app.swig_ignores for class_name, cls in classes.iteritems(): cls_config = cls.cls_config script_object_member = cls_config.get('script_object_member', settings.default_script_object_member) #print class_name #print cls.super_classes base_cb_class = None fscls = class_name while fscls: fscls = classes[fscls].find_super_class(classes, settings.classes) if fscls: base_cb_class = fscls if ((not class_name in settings.classes and not base_cb_class) or class_name in swig_ignores): continue class_name_orig = class_name py_class_name = None for fs,ss in settings.class_renamers: re_fs = re.compile(fs) if re_fs.match(class_name): py_class_name = re_fs.sub(ss, class_name) break if not py_class_name: py_class_name = 'Py%s'%class_name print "Creating callback class for '%s' ('%s')"%(class_name,py_class_name) hstatics = [] hdecls = [] hdecls2 = [] sstatics = [] simps = [] plain_functions = [] sstaticsets = [] constructors = [] cdecls = [] cimps = [] # # Look for additional functions to implement (from add_wrapper_code-section) plain_functions_to_add = [] for awc_class_name, awc_cls in add_wrapper_code_classes.iteritems(): if class_name == awc_class_name or cls.find_super_class(classes, [awc_class_name]): for func in awc_cls.func_list: if not ('pyvirtual' in func.flags): plain_functions_to_add.append(func) for func in plain_functions_to_add: fpp = func.decl.find(func.name)-1 rest_of_decl = func.decl[fpp+1:] hdecls.append(' %s'%func.construct_decl()) plain_functions.append(func) plain_function_names = set([func.name for func in plain_functions_to_add]) # Add functions from parent callback classes use_func_list = cls.get_all_functions(classes) existing_func_names = set([func.name for func in use_func_list]) use_func_list.extend(cls.additional_virtual_functions) use_func_list.sort(lambda x,y: cmp(x.name, y.name)) template_vars = {'py_class_name':py_class_name, 'class_name':class_name_orig, 'script_object_member':script_object_member} for func in use_func_list: func_name = func.name #if class_name == 'wxPGTextCtrlAndButtonEditor': # print '%s flags: %s in_this: %s'%(func_name,repr(func.flags),(func_name in cls.func_names)) fpp = func.decl.find(func_name)-1 rest_of_decl = func.decl[fpp+1:] ppp = func.decl.find('(') args_of_decl = func.decl[ppp:] arguments = func.arguments args_str_nodefs = ', '.join(['%s %s'%(a[0],a[1]) for a in arguments]) arg_names_list = ', '.join([a[1] for a in arguments]) func_post_decl = '' if 'const' in func.flags: func_post_decl = ' const' func_decl_impl = '%s(%s)%s'%(func_name, args_str_nodefs, func_post_decl) if arguments: args_str_nodefs_cont = ', '+args_str_nodefs else: args_str_nodefs_cont = '' # # Constructor if func_name == class_name: cdecls.append(' %s%s;'%(py_class_name,args_of_decl)) ss = [] ss.append('\n%s::%s(%s)'%(py_class_name,py_class_name,args_str_nodefs)) ss.append(' : %s(%s)'%(class_name,arg_names_list)) ss.append('{') if settings.logging: ss.append(' %s(wxT("%s::%s()"));\n'%(dbg_func,py_class_name,py_class_name)) ss.append(' Init();') ss.append('}') cimps.append('\n'.join(ss)) continue # Don't add non-virtual methods or destructors if func_name[0] == '~' or not 'pyvirtual' in func.flags: continue if func_name in plain_function_names: raise AssertionError("%s::%s() defined in add_wrapper_code-section conflicted " \ "with virtual function with same name"%(class_name, func_name)) if not class_name in app.callback_classes: app.add_callback_class(class_name, py_class_name, script_object_member) # Determine name with which this functions is exposed in Python py_func_name = func_name if func_name.startswith('Py') and func_name[2].isupper(): py_func_name = func_name[2:] v_py_funcnames.add(py_func_name) # Add dummy names instead of empty names, where needed. for i in range(0,len(arguments)): a = arguments[i] if not a[1]: print 'ERROR: You must specify names for all arguments for %s::%s'%(class_name,func.name) #arguments[i] = (a[0],'_arg%i'%i) return if func.retval == 'void': rets_void = True retvals_count = 0 default_return = 'return;' ret_with_func = '' else: rets_void = False retvals_count = 1 default_return = 'return %s;'%(cpp_header_parser.get_default_value_for_type(func.retval)) ret_with_func = 'return ' if not 'pure' in func.flags: add_pfunc = True else: add_pfunc = False decl_s = ' virtual %s;'%(func.decl) # If it was pure, we need to add it to interface as well, so that SWIG knows the # class is, infact non-abstract now. if 'pure' in func.flags: hdecls2.append(decl_s) hdecls.append(decl_s) pfimp = '' """ add_pfunc = False if add_pfunc: s = ' %s %s%s%s;'%(func.retval,func.name,pfunc_addendum,args_of_decl) hdecls.append(s) hdecls2.append(s) ss = [] ss.append('') ss.append('%s %s::%s%s(%s)%s { %s%s::%s(%s); }'%(func.retval,py_class_name,func.name,pfunc_addendum,args_str_nodefs,func_post_decl,ret_with_func,class_name,func.name,arg_names_list)) pfimp = '\n'.join(ss) else: pfimp = '' """ # # Generate wrapper function implementation code # # Generate implementation # si = [] si.append('') si.append('%s %s::%s'%(func.retval,py_class_name,func_decl_impl)) si.append('{') si.append(' wxPyBlock_t blocked = wxPyBeginBlockThreads();') if settings.logging: si.append(' %s(wxT("%s::%s() entry"));'%(dbg_func,py_class_name,func.name)) si.append('') # # Fall-back check si.append(' PyObject* cls_ = PyObject_GetAttr((PyObject*)%s, gs___class___Name);'%(script_object_member)) si.append(' PyObject* funcobj = NULL;') si.append(' if ( PyObject_HasAttr(cls_, gs_%s_Name) == 1 ) funcobj = PyObject_GetAttr(cls_, gs_%s_Name);'%(py_func_name, py_func_name)) si.append(' Py_DECREF(cls_);') si.append(' if ( !funcobj || PyObject_HasAttr((PyObject*)%s, gs__super_call_Name) == 1 )'%(script_object_member)) #si.append(' if ( PyObject_HasAttr(cls_, gs_%s_Name) != 1 || PyObject_HasAttr(m_scriptObject, gs__super_call_Name) == 1 )'%(py_func_name)) si.append(' {') si.append(' wxPyEndBlockThreads(blocked);') if not 'pure' in func.flags: if settings.logging: si.append(' %s(wxT("%s::%s() exit (fall-back)"));'%(dbg_func,py_class_name,func.name)) # If class implemented base version, return it if func_name in existing_func_names: if not rets_void: si.append(' return %s::%s(%s);'%(class_name,func.name,arg_names_list)) else: si.append(' %s::%s(%s);'%(class_name,func.name,arg_names_list)) si.append(' return;') else: # Apply code from version in add_wrapper_code # (available as func.content) base_func_content = func.content if not base_func_content: raise AssertionError('%s::%s() should have function content'%(class_name, func_name)) si.append(cpp_header_parser.indent(base_func_content%template_vars, 8)) else: if settings.logging: si.append(' %s(wxT("%s::%s() exit (not implemented!!!)"));'%(dbg_func,py_class_name,func.name)) si.append(' PyErr_SetString(PyExc_TypeError,"this method must be implemented");') si.append(' %s'%default_return) si.append(' }') #si.append(' PyObject* funcobj = PyObject_GetAttr(cls_, gs_%s_Name);'%(py_func_name)) #if settings.logging: si.append(' %s("6");'%dbg_func) pre_func = '\n'.join(si) si = [] # Prepare arguments prepss, unprepss, retvalprepss, signature = app.get_swig_typeconv_out(arguments, is_callback = True) argin_typemaps = len([a_ for a_ in retvalprepss if a_]) retvals_count += argin_typemaps if retvals_count > 1: pyrvn = 'tpl' else: pyrvn = 'res' si.append(' PyObject* %s;'%pyrvn) # Map new argument list according to callback typemaps used used_args = [] for i in range(0,len(arguments)): if prepss[i]: used_args.append(arguments[i]) # Form typemap-based function signature signature = func.retval + signature simps.append((signature,pre_func,func,rets_void,pfimp)) # Don't redo this method, if common parts for function with this signature was already done if signature in funcsigmap: funcsigmap[signature][3] += 1 continue i = 0 for arg_type,arg_name in arguments: ps = prepss[i] if ps: si.append(' PyObject* py_%s;'%arg_name) si.append('%s'%ps) i += 1 #si.append('%s'%app.get_swig_typeconv_out(arg_type, arg_name,'py_%s'%arg_name)) # Python method call #si.append('\n PyObject* %s = PyObject_CallMethodObjArgs(self, funcname,'%(pyrvn)) if used_args: s_ = ''.join(['py_%s, '%(a[1]) for a in used_args]) else: s_ = '' si.append(' %s = PyObject_CallFunctionObjArgs(funcobj, self, %sNULL);'%(pyrvn,s_)) """ for arg_type,arg_name in used_args: si.append(' py_%s,'%arg_name) si.append(' NULL);\n') """ si.append(' Py_DECREF(funcobj);') # Unprepare arguments unprepss.reverse() lines_added = 0 for ps in unprepss: if ps: si.append('%s'%ps) lines_added += 1 #if lines_added: # si.append('') si.append(' if (PyErr_Occurred()) SWIG_fail;') si.append(' {') # # Return value conversion if not rets_void: in_prepss, in_unprepss, in_retvalprepss = app.get_swig_typeconv_in([(func.retval,'retval')], is_callback = True) si.append(' %s retval;'%func.retval) # If argin values were met, we need to prepare to process a sequence if retvals_count > 1: si.append(' long tpl_count = -1;') si.append(' if ( PySequence_Check(tpl) ) {') si.append(' PyObject* py_tpl_count = PyInt_FromLong((long)0);') si.append(' PySequence_Count(tpl, py_tpl_count);') si.append(' tpl_count = PyInt_AsLong(py_tpl_count);') si.append(' Py_DECREF(py_tpl_count);') si.append(' }') si.append(' if ( tpl_count != %i ) {'%retvals_count) si.append(' Py_DECREF(tpl);') si.append(' PyErr_SetString(PyExc_TypeError, "Expected tuple of %i items as a return value.");'%retvals_count) si.append(' SWIG_fail;') si.append(' }') si.append('') si.append(' PyObject* res;') ret_handlers = [] if not rets_void: ret_handlers.append(in_prepss[0]) ret_handlers.extend(retvalprepss) i = 0 for ps in ret_handlers: if ps: si.append(' res = PySequence_GetItem(%s, %i);'%(pyrvn,i)) si.append(ps) si.append('') i += 1 si.append(' Py_DECREF(tpl);') elif retvals_count == 1: si.append(in_prepss[0]) else: # DecRef the None-result si.append(' Py_DECREF(res);') # Check if we need to add fail-label need_fail = True #need_fail = False #for s in si: # if s.find('goto fail') >= 0 or s.find('SWIG_fail') >= 0 or s.find('SWIG_exception_fail') >= 0: # need_fail = True # break # Finalize if not rets_void: si.append(' wxPyEndBlockThreads(blocked);') si.append(' return retval;') si.append(' }') if need_fail: si.append(' fail:') si.append(' if ( PyErr_Occurred() ) PyErr_Print();') si.append(' wxPyEndBlockThreads(blocked);') si.append(' %s'%default_return); else: si.append(' }') if need_fail: si.append(' fail:') si.append(' wxPyEndBlockThreads(blocked);') sig_num = len(funcsigmap) """rod2 = rest_of_decl[rest_of_decl.find('(')+1:] if rod2.find(')') > 0: rod2 = (', '+rod2).replace(' ',' ') if rod2.endswith('const'): rod2 = rod2[:-5]""" #common_func_start = '%s _CommonCallback%i(wxPyBlock_t blocked, PyObject* self, PyObject* funcname%s)'%(func.retval,sig_num,args_str_nodefs_cont) common_func_start = '%s _CommonCallback%i(wxPyBlock_t blocked, PyObject* self, PyObject* funcobj%s)'%(func.retval,sig_num,args_str_nodefs_cont) funcsigmap[signature] = [sig_num,common_func_start,'\n'.join(si),1] if not cdecls: cdecls.append(' %s();'%py_class_name) ss = [] ss.append('\n%s::%s()'%(py_class_name,py_class_name)) ss.append(' : %s()'%(class_name)) ss.append('{') if settings.logging: ss.append(' %s(wxT("%s::%s()"));'%(dbg_func,py_class_name,py_class_name)) ss.append(' Init();') ss.append('}') cimps.append('\n'.join(ss)) if settings.logging: dtor_log = '\n %s(wxT("%s::~%s()"));'%(dbg_func,py_class_name,py_class_name) else: dtor_log = '' template_vars.update({'func_decls':'\n'.join(hdecls),'ctor_decls':'\n'.join(cdecls), 'ctor_imps':'\n'.join(cimps), 'dtor_log':dtor_log}) # Interface-class code scl = app.scl scl.append('') scl.append('class %s : public %s'%(py_class_name,class_name)) scl.append('{') scl.append('public:') if '__init__append' in cls_config: init_append_s = '; %s' % cls_config['__init__append'] else: init_append_s = '' scl.append(' %%pythonAppend %s "self._SetSelf(self); self._RegisterMethods()%s"' % (py_class_name, init_append_s)) scl.append(template_vars['ctor_decls']) scl.append('%pythoncode {') scl.append(' def CallSuperMethod(self, *args, **kwargs):') scl.append(' funcname = args[0]') scl.append(' args2 = list(args)') scl.append(' args2[0] = self') scl.append(' self._super_call = True') scl.append(' try:') scl.append(' res = getattr(%s, funcname)(*args2, **kwargs)'%py_class_name) scl.append(' finally:') scl.append(' del self._super_call') scl.append(' return res') scl.append('') scl.append(' def _RegisterMethods(self):') scl.append(' cls = self.__class__') scl.append(' if not hasattr(cls,\'_pyswig_methods_registered\'):') scl.append(' cls._pyswig_methods_registered = True') scl.append(' ls = [ab for ab in cls.__dict__.iteritems()]') scl.append(' for a, b in ls:') scl.append(' if not a.startswith(\'_\'):') scl.append(' setattr(cls, \'%%s%s\'%%a, b)'%func_appendum) scl.append('}') scl.append(' void _SetSelf(PyObject *self);') for s_ in hdecls2: scl.append(s_) # Add code that copies function-objects to self scl.append('};') class_code = """\ class %(py_class_name)s : public %(class_name)s { public: %(ctor_decls)s virtual ~%(py_class_name)s(); void _SetSelf(PyObject *self); %(func_decls)s private: void Init() { if ( !gs_funcNamesInitialized ) _InitFuncNames(); } }; static PyObject* gs_%(py_class_name)s_pyClass = NULL; %(ctor_imps)s %(py_class_name)s::~%(py_class_name)s() {%(dtor_log)s if (%(script_object_member)s) { _deleteOwningObject(%(script_object_member)s); %(script_object_member)s = NULL; } } void %(py_class_name)s::_SetSelf(PyObject *self) { if ( !gs_%(py_class_name)s_pyClass ) { gs_%(py_class_name)s_pyClass = PyObject_GetAttr(self, gs___class___Name); Py_DECREF(gs_%(py_class_name)s_pyClass); } if ( !%(script_object_member)s ) { %(script_object_member)s = self; Py_INCREF(self); } } """%template_vars class_codes.append((class_code, py_class_name, script_object_member, simps, plain_functions, template_vars)) ss = ['// THIS FILE HAS BEEN AUTO-GENERATED BY %s' % \ (SCRIPT_NAME.upper())] # # Add some helper functions s = '''\ #ifndef SWIG_IsOK #define SWIG_IsOK(r) (r >= 0) #endif #ifndef Py_RETURN_NONE #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None #endif void _deleteOwningObject(void* obj) { // Crashes sometimes (on app exit, it seems), so we need to disable it /*if ( Py_IsInitialized() ) { wxPyBlock_t blocked = wxPyBeginBlockThreads(); Py_XDECREF((PyObject*)obj); wxPyEndBlockThreads(blocked); }*/ } ''' ss.append(s) # # Add code to manage static python string representations of function names v_py_funcnames = list(v_py_funcnames) v_py_funcnames.sort() #ss.append('#include <Streams.h>') ss.append('') ss.append('static bool gs_funcNamesInitialized = false;') ss.append('static PyObject* gs___class___Name = NULL;') ss.append('static PyObject* gs___dict___Name = NULL;') ss.append('static PyObject* gs__super_call_Name = NULL;') # Declarations for s in v_py_funcnames: ss.append('static PyObject* gs_%s_Name = NULL;'%s) ss.append('') ss.append('static void _InitFuncNames()') ss.append('{') ss.append(' gs___dict___Name = PyString_FromString("__dict__");') ss.append(' gs___class___Name = PyString_FromString("__class__");') ss.append(' gs__super_call_Name = PyString_FromString("_super_call");') for s in v_py_funcnames: ss.append(' gs_%s_Name = PyString_FromString("%s%s");'%(s,s,func_appendum)) ss.append(' gs_funcNamesInitialized = true;') ss.append('}') ss.append('') # Generate shared callback function implementations for signature,tpl in funcsigmap.iteritems(): sig_num, common_func_start, func_imp, sig_count = tpl if sig_count > 1: # Shared implementation ss.append('') ss.append('%s'%common_func_start) ss.append('{') ss.append(func_imp) ss.append('}') # Generate callback class code for s, py_class_name, script_object_member, simps, plain_functions, template_vars in class_codes: ss.append(s) for func in plain_functions: ss.append(func.construct_impl_head(class_name=py_class_name)) ss.append(func.content%template_vars) ss.append('') for signature, pre_func, func, rets_void, pfimp in simps: sig_num, common_func_start, func_imp, sig_count = funcsigmap[signature] ss.append(pre_func) if sig_count > 1: # Shared implementation if not rets_void: ret_with_func = 'return ' else: ret_with_func = '' #cc_call_pre = '_CommonCallback%i(blocked, m_scriptObject, gs_%s_Name'%(sig_num,func.name) cc_call_pre = '%s_CommonCallback%i(blocked, (PyObject*)%s, funcobj'%(ret_with_func,sig_num, script_object_member) if len(func.arguments) > 0: ss.append(' %s, %s);'%(cc_call_pre,', '.join([a[1] for a in func.arguments]))) else: ss.append(' %s);'%cc_call_pre) else: # Private implementation ss.append(func_imp.replace('self','((PyObject*)%s)'%script_object_member).replace('funcname','gs_%s_Name'%func.name)) if settings.logging: ss.append(' %s(wxT("%s::%s() exit"));'%(dbg_func,py_class_name,func.name)) ss.append('}') ss.append(pfimp) # Wrap it inside "SWIG-C-code-indicators", so the code can be included # when running SWIG projname.i. s = '%%{\n\n%s\n\n%%}\n'%('\n'.join(ss)) f = file(os.path.join(settings.output_dir,'%s_cbacks.cpp'%settings.projname),'wt') f.write(s) f.close() s = '\n'.join(app.scl) f = file(os.path.join(settings.output_dir,'%s_cbacks.i'%settings.projname),'wt') f.write(s) f.close() out_typemaps = app.typemaps['in'].keys() out_typemaps.sort()
def process_include_files(self): from cpp_header_parser import split_argument_list, indent from cpp_header_parser import find_balanced_parenthesis re_typemap = re.compile('%typemap\s*\(\s*([^()]+)\s*\)([^{]+){',re.I) for fn in settings.includes: print("Loading include '%s'" % fn) s = self.load_file(fn) if s: s = cpp_header_parser.purge_comments(s) # Find C++ defines for m in re_cdefine.finditer(s): #{ c_define = m.group(0).strip() if c_define[-1] == '\\': #{ raise NotImplementedError('Multi-line defines not ' 'yet supported') #} self.c_define_lines.append(c_define) #} # Find SWIG ignore directives for m in re_swig_ignore.finditer(s): #{ self.swig_ignores.add(m.group(1).strip()) #} s = cpp_header_parser.process_and_run_macros(s, settings.include_paths, is_swig=True) # Load typemaps pos = 0 while 1: m = re_typemap.search(s, pos) if not m: break # Get type options tts = [s_.strip() for s_ in m.group(1).split(',')] tm_type = tts[0] tm_options = tts[1:] tm_sdatatype = m.group(2) parens_pos = tm_sdatatype.find('(') if parens_pos >= 0: # There is either multiple datatypes, or temp arg list. pre_p = tm_sdatatype[:parens_pos].strip() if len(pre_p) > 0: # Temp arg list tm_datatypes = split_argument_list(pre_p) tm_tempdata,i_ = split_argument_list(tm_sdatatype, parens_pos, def_vals = True) else: # Multiple args tm_datatypes,i_ = split_argument_list(tm_sdatatype, parens_pos) tm_tempdata = [] else: tm_datatypes = split_argument_list(tm_sdatatype) tm_tempdata = [] tm_start = m.end()-1 tm_end = find_balanced_parenthesis(s, tm_start, '{', '}') tm_content = indent(s[tm_start+1:tm_end-1],4).rstrip() #print tm_type, tm_datatypes #print tm_options #print tm_tempdata #print tm_content # # FIXME: This is a temporary fix - override all wxVariant # typemaps with those found in our own files. ignore_this = False if fn == 'my_typemaps.i': for ig_typemap in settings.ignore_typemaps_from_base: for tn, vn in tm_datatypes: if tn.startswith(ig_typemap): print('IGNORED %s typemap from %s' % \ (tm_datatypes, fn)) ignore_this = True break if ignore_this: break # Do not store options, they are usually just # useless precedence=SWIG_TYPECHECK_POINTER for typecheck. if not ignore_this: self.add_typemap(tm_type, tm_datatypes, tm_tempdata, tm_content) pos = m.end()