1## -*- python -*-
2## pylint: disable-msg=W0142,R0921
3
4"""
5Base classes for all parameter/return type handlers,
6and base interfaces for wrapper generators.
7"""
8
9from pybindgen.typehandlers import codesink
10import warnings
11from pybindgen.typehandlers import ctypeparser
12import sys
13
14PY3 = (sys.version_info[0] >= 3)
15if PY3:
16    import types
17    string_types = str,
18else:
19    string_types = basestring
20
21import logging
22logger = logging.getLogger("pybindgen.typehandlers")
23
24
25try:
26    all
27except NameError: # for compatibility with Python < 2.5
28    def all(iterable):
29        "Returns True if all elements are true"
30        for element in iterable:
31            if not element:
32                return False
33        return True
34try:
35    set
36except NameError:
37    from sets import Set as set   # Python 2.3 fallback
38
39
40
41
42class CodegenErrorBase(Exception):
43    pass
44
45class NotSupportedError(CodegenErrorBase):
46    """Exception that is raised when declaring an interface configuration
47    that is not supported or not implemented."""
48
49class CodeGenerationError(CodegenErrorBase):
50    """Exception that is raised when wrapper generation fails for some reason."""
51
52class TypeLookupError(CodegenErrorBase):
53    """Exception that is raised when lookup of a type handler fails"""
54
55class TypeConfigurationError(CodegenErrorBase):
56    """Exception that is raised when a type handler does not find some
57    information it needs, such as owernship transfer semantics."""
58
59
60def join_ctype_and_name(ctype, name):
61    """
62    Utility method that joins a C type and a variable name into
63    a single string
64
65    >>> join_ctype_and_name('void*', 'foo')
66    'void *foo'
67    >>> join_ctype_and_name('void *', 'foo')
68    'void *foo'
69    >>> join_ctype_and_name("void**", "foo")
70    'void **foo'
71    >>> join_ctype_and_name("void **", "foo")
72    'void **foo'
73    >>> join_ctype_and_name('C*', 'foo')
74    'C *foo'
75    """
76    if ctype[-1] == '*':
77        for i in range(-1, -len(ctype) - 1, -1):
78            if ctype[i] != '*':
79                if ctype[i] == ' ':
80                    return "".join([ctype[:i+1], ctype[i+1:], name])
81                else:
82                    return "".join([ctype[:i+1], ' ', ctype[i+1:], name])
83        raise ValueError((ctype, name))
84    else:
85        return " ".join([ctype, name])
86
87
88class CodeBlock(object):
89    '''An intelligent code block that keeps track of cleanup actions.
90    This object is to be used by TypeHandlers when generating code.'''
91
92    class CleanupHandle(object):
93        """Handle for some cleanup code"""
94        __slots__ = ['code_block', 'position']
95        def __init__(self, code_block, position):
96            """Create a handle given code_block and position"""
97            self.code_block = code_block
98            self.position = position
99
100        def __cmp__(self, other):
101            comp = cmp(self.code_block, other.code_block)
102            if comp:
103                return comp
104            return cmp(self.position, other.position)
105
106        def cancel(self):
107            """Cancel the cleanup code"""
108            self.code_block.remove_cleanup_code(self)
109
110        def get_position(self):
111            "returns the cleanup code relative position"
112            return self.position
113
114
115    def __init__(self, error_return, declarations, predecessor=None):
116        '''
117        CodeBlock constructor
118
119        >>> block = CodeBlock("return NULL;", DeclarationsScope())
120        >>> block.write_code("foo();")
121        >>> cleanup1 = block.add_cleanup_code("clean1();")
122        >>> cleanup2 = block.add_cleanup_code("clean2();")
123        >>> cleanup3 = block.add_cleanup_code("clean3();")
124        >>> cleanup2.cancel()
125        >>> block.write_error_check("error()", "error_clean()")
126        >>> block.write_code("bar();")
127        >>> block.write_cleanup()
128        >>> print block.sink.flush().rstrip()
129        foo();
130        if (error()) {
131            error_clean()
132            clean3();
133            clean1();
134            return NULL;
135        }
136        bar();
137        clean3();
138        clean1();
139
140        :param error_return: code that is generated on error conditions
141                           (detected by write_error_check()); normally
142                           it returns from the wrapper function,
143                           e.g. return NULL;
144        :param predecessor: optional predecessor code block; a
145                          predecessor is used to search for additional
146                          cleanup actions.
147        '''
148        assert isinstance(declarations, DeclarationsScope)
149        assert predecessor is None or isinstance(predecessor, CodeBlock)
150        self.sink = codesink.MemoryCodeSink()
151        self.predecessor = predecessor
152        self._cleanup_actions = {}
153        self._last_cleanup_position = 0
154        self.error_return = error_return
155        self.declarations = declarations
156
157    def clear(self):
158        self._cleanup_actions = {}
159        self._last_cleanup_position = 0
160        self.sink = codesink.MemoryCodeSink()
161
162    def declare_variable(self, type_, name, initializer=None, array=None):
163        """
164        Calls declare_variable() on the associated DeclarationsScope object.
165        """
166        if ':' in name:
167            raise ValueError("invalid variable name: %s " % name)
168        return self.declarations.declare_variable(type_, name, initializer, array)
169
170    def write_code(self, code):
171        '''Write out some simple code'''
172        self.sink.writeln(code)
173
174    def indent(self, level=4):
175        '''Add a certain ammount of indentation to all lines written
176        from now on and until unindent() is called'''
177        self.sink.indent(level)
178
179    def unindent(self):
180        '''Revert indentation level to the value before last indent() call'''
181        self.sink.unindent()
182
183    def add_cleanup_code(self, cleanup_code):
184        '''Add a chunk of code used to cleanup previously allocated resources
185
186        Returns a handle used to cancel the cleanup code
187        '''
188        self._last_cleanup_position += 1
189        handle = self.CleanupHandle(self, self._last_cleanup_position)
190        self._cleanup_actions[handle.get_position()] = cleanup_code
191        return handle
192
193    def remove_cleanup_code(self, handle):
194        '''Remove cleanup code previously added with add_cleanup_code()
195        '''
196        assert isinstance(handle, self.CleanupHandle)
197        del self._cleanup_actions[handle.get_position()]
198
199    def get_cleanup_code(self):
200        '''return a new list with all cleanup actions, including the
201        ones from predecessor code blocks; Note: cleanup actions are
202        executed in reverse order than when they were added.'''
203        cleanup = []
204        items = list(self._cleanup_actions.items())
205        items.sort()
206        for dummy, code in items:
207            cleanup.append(code)
208        cleanup.reverse()
209        if self.predecessor is not None:
210            cleanup.extend(self.predecessor.get_cleanup_code())
211        return cleanup
212
213    def write_error_check(self, failure_expression, failure_cleanup=None):
214        '''Add a chunk of code that checks for a possible error
215
216        :param failure_expression: C boolean expression that is true when
217                              an error occurred
218        :param failure_cleanup: optional extra cleanup code to write only
219                           for the the case when failure_expression is
220                           true; this extra cleanup code comes before
221                           all other cleanup code previously registered.
222        '''
223        self.sink.writeln("if (%s) {" % (failure_expression,))
224        self.sink.indent()
225        if failure_cleanup is not None:
226            self.sink.writeln(failure_cleanup)
227        self.write_error_return()
228        self.sink.unindent()
229        self.sink.writeln("}")
230
231    def write_cleanup(self):
232        """Write the current cleanup code."""
233        for cleanup_action in self.get_cleanup_code():
234            self.sink.writeln(cleanup_action)
235
236    def write_error_return(self):
237        '''Add a chunk of code that cleans up and returns an error.
238        '''
239        self.write_cleanup()
240        self.sink.writeln(self.error_return)
241
242
243
244class ParseTupleParameters(object):
245    "Object to keep track of PyArg_ParseTuple (or similar) parameters"
246
247    def __init__(self):
248        """
249        >>> tuple_params = ParseTupleParameters()
250        >>> tuple_params.add_parameter('i', ['&foo'], 'foo')
251        1
252        >>> tuple_params.add_parameter('s', ['&bar'], 'bar', optional=True)
253        2
254        >>> tuple_params.get_parameters()
255        ['"i|s"', '&foo', '&bar']
256        >>> tuple_params.get_keywords()
257        ['foo', 'bar']
258
259        >>> tuple_params = ParseTupleParameters()
260        >>> tuple_params.add_parameter('i', ['&foo'], 'foo')
261        1
262        >>> tuple_params.add_parameter('s', ['&bar'], 'bar', prepend=True)
263        2
264        >>> tuple_params.get_parameters()
265        ['"si"', '&bar', '&foo']
266        >>> tuple_params.get_keywords()
267        ['bar', 'foo']
268
269        >>> tuple_params = ParseTupleParameters()
270        >>> tuple_params.add_parameter('i', ['&foo'])
271        1
272        >>> print tuple_params.get_keywords()
273        None
274        """
275        self._parse_tuple_items = [] # (template, param_values, param_name, optional)
276
277    def clear(self):
278        self._parse_tuple_items = []
279
280    def add_parameter(self, param_template, param_values, param_name=None,
281                      prepend=False, optional=False):
282        """
283        Adds a new parameter specification
284
285        :param param_template: template item, see documentation for
286                          PyArg_ParseTuple for more information
287        :param param_values: list of parameters, see documentation
288                       for PyArg_ParseTuple for more information
289        :param prepend: whether this parameter should be parsed first
290        :param optional: whether the parameter is optional; note that after
291                    the first optional parameter, all remaining
292                    parameters must also be optional
293        """
294        assert isinstance(param_values, list)
295        assert isinstance(param_template, string_types)
296        item = (param_template, param_values, param_name, optional)
297        if prepend:
298            self._parse_tuple_items.insert(0, item)
299        else:
300            self._parse_tuple_items.append(item)
301        return len(self._parse_tuple_items)
302
303    def is_empty(self):
304        return self.get_parameters() == ['""']
305
306    def get_parameters(self):
307        """
308        returns a list of parameters to pass into a
309        PyArg_ParseTuple-style function call, the first paramter in
310        the list being the template string.
311        """
312        template = ['"']
313        last_was_optional = False
314        for (param_template, dummy,
315             param_name, optional) in self._parse_tuple_items:
316            if last_was_optional and not optional:
317                raise ValueError("Error: optional parameter followed by a non-optional one (%r)"
318                                 " (debug: self._parse_tuple_parameters=%r)" % (param_name, self._parse_tuple_items))
319            if not last_was_optional and optional:
320                template.append('|')
321                last_was_optional = True
322            template.append(param_template)
323        template.append('"')
324        params = [''.join(template)]
325        for (dummy, param_values,
326             dummy, dummy) in self._parse_tuple_items:
327            params.extend(param_values)
328        return params
329
330    def get_keywords(self):
331        """
332        returns list of keywords (parameter names), or None if none of
333        the parameters had a name; should only be called if names were
334        given for all parameters or none of them.
335        """
336        keywords = []
337        for (dummy, dummy, name, dummy) in self._parse_tuple_items:
338            if name is None:
339                if keywords:
340                    raise ValueError("mixing parameters with and without keywords")
341            else:
342                keywords.append(name)
343        if keywords:
344            if len(keywords) != len(self._parse_tuple_items):
345                raise ValueError("mixing parameters with and without keywords")
346            return keywords
347        else:
348            return None
349
350
351class BuildValueParameters(object):
352    "Object to keep track of Py_BuildValue (or similar) parameters"
353
354    def __init__(self):
355        """
356        >>> bld = BuildValueParameters()
357        >>> bld.add_parameter('i', [123, 456])
358        >>> bld.add_parameter('s', ["hello"])
359        >>> bld.get_parameters()
360        ['"is"', 123, 456, 'hello']
361        >>> bld = BuildValueParameters()
362        >>> bld.add_parameter('i', [123])
363        >>> bld.add_parameter('s', ["hello"], prepend=True)
364        >>> bld.get_parameters()
365        ['"si"', 'hello', 123]
366        """
367        self._build_value_items = [] # (template, param_value, cleanup_handle)
368
369    def clear(self):
370        self._build_value_items = []
371
372    def add_parameter(self, param_template, param_values,
373                      prepend=False, cancels_cleanup=None):
374        """
375        Adds a new parameter to the Py_BuildValue (or similar) statement.
376
377        :param param_template: template item, see documentation for
378                          Py_BuildValue for more information
379        :param param_values: list of C expressions to use as value, see documentation
380                        for Py_BuildValue for more information
381        :param prepend: whether this parameter should come first in the tuple being built
382        :param cancels_cleanup: optional handle to a cleanup action,
383                           that is removed after the call.  Typically
384                           this is used for 'N' parameters, which
385                           already consume an object reference
386        """
387        item = (param_template, param_values, cancels_cleanup)
388        if prepend:
389            self._build_value_items.insert(0, item)
390        else:
391            self._build_value_items.append(item)
392
393    def get_parameters(self, force_tuple_creation=False):
394        """returns a list of parameters to pass into a
395        Py_BuildValue-style function call, the first paramter in
396        the list being the template string.
397
398        :param force_tuple_creation: if True, Py_BuildValue is
399           instructed to always create a tuple, even for zero or 1
400           values.
401        """
402        template = ['"']
403        if force_tuple_creation:
404            template.append('(')
405        params = [None]
406        for (param_template, param_values, dummy) in self._build_value_items:
407            template.append(param_template)
408            params.extend(param_values)
409        if force_tuple_creation:
410            template.append(')')
411        template.append('"')
412        params[0] = ''.join(template)
413        return params
414
415    def get_cleanups(self):
416        """Get a list of handles to cleanup actions"""
417        return [cleanup for (dummy, dummy, cleanup) in self._build_value_items]
418
419
420class DeclarationsScope(object):
421    """Manages variable declarations in a given scope."""
422
423    def __init__(self, parent_scope=None):
424        """
425        Constructor
426
427        >>> scope = DeclarationsScope()
428        >>> scope.declare_variable('int', 'foo')
429        'foo'
430        >>> scope.declare_variable('char*', 'bar')
431        'bar'
432        >>> scope.declare_variable('int', 'foo')
433        'foo2'
434        >>> scope.declare_variable('int', 'foo', '1')
435        'foo3'
436        >>> scope.declare_variable('const char *', 'kwargs', '{"hello", NULL}', '[]')
437        'kwargs'
438        >>> print scope.get_code_sink().flush().rstrip()
439        int foo;
440        char *bar;
441        int foo2;
442        int foo3 = 1;
443        const char *kwargs[] = {"hello", NULL};
444
445        :param parent_scope: optional 'parent scope'; if given,
446                        declarations in this scope will avoid clashing
447                        with names in the parent scope, and vice
448                        versa.
449        """
450        self._declarations = codesink.MemoryCodeSink()
451        ## name -> number of variables with that name prefix
452        if parent_scope is None:
453            self.declared_variables = {}
454        else:
455            assert isinstance(parent_scope, DeclarationsScope)
456            self.declared_variables = parent_scope.declared_variables
457
458    def clear(self):
459        self._declarations = codesink.MemoryCodeSink()
460        self.declared_variables.clear()
461
462    def declare_variable(self, type_, name, initializer=None, array=None):
463        """Add code to declare a variable. Returns the actual variable
464        name used (uses 'name' as base, with a number in case of conflict.)
465
466        :param type_: C type name of the variable
467        :param name: base name of the variable; actual name used can be
468                slightly different in case of name conflict.
469        :param initializer: optional, value to initialize the variable with
470        :param array: optional, array size specifiction, e.g. '[]', or '[100]'
471        """
472        try:
473            num = self.declared_variables[name]
474        except KeyError:
475            num = 0
476        num += 1
477        self.declared_variables[name] = num
478        if num == 1:
479            varname = name
480        else:
481            varname = "%s%i" % (name, num)
482        decl = join_ctype_and_name(type_, varname)
483        if array is not None:
484            decl += array
485        if initializer is not None:
486            decl += ' = ' + initializer
487        self._declarations.writeln(decl + ';')
488        return varname
489
490    def reserve_variable(self, name):
491        """Reserve a variable name, to be used later.
492
493        :param name: base name of the variable; actual name used can be
494                slightly different in case of name conflict.
495        """
496        try:
497            num = self.declared_variables[name]
498        except KeyError:
499            num = 0
500        num += 1
501        self.declared_variables[name] = num
502        if num == 1:
503            varname = name
504        else:
505            varname = "%s%i" % (name, num)
506        return varname
507
508    def get_code_sink(self):
509        """Returns the internal MemoryCodeSink that holds all declararions."""
510        return self._declarations
511
512
513
514class ReverseWrapperBase(object):
515    """Generic base for all reverse wrapper generators.
516
517    Reverse wrappers all have the following general structure in common:
518
519     1. 'declarations' -- variable declarations; for compatibility with
520        older C compilers it is very important that all declarations
521        come before any simple statement.  Declarations can be added
522        with the add_declaration() method on the 'declarations'
523        attribute.  Two standard declarations are always predeclared:
524        '<return-type> retval', unless return-type is void, and 'PyObject
525        \\*py_retval';
526
527     2. 'code before call' -- this is a code block dedicated to contain
528        all code that is needed before calling into Python; code can be
529        freely added to it by accessing the 'before_call' (a CodeBlock
530        instance) attribute;
531
532     3. 'call into python' -- this is realized by a
533        PyObject_CallMethod(...) or similar Python API call; the list
534        of parameters used in this call can be customized by accessing
535        the 'build_params' (a BuildValueParameters instance) attribute;
536
537     4. 'code after call' -- this is a code block dedicated to contain
538        all code that must come after calling into Python; code can be
539        freely added to it by accessing the 'after_call' (a CodeBlock
540        instance) attribute;
541
542     5. A 'return retval' statement (or just 'return' if return_value is void)
543
544    """
545
546    NO_GIL_LOCKING = False
547
548    def __init__(self, return_value, parameters, error_return=None):
549        '''
550        Base constructor
551
552        :param return_value: type handler for the return value
553        :param parameters: a list of type handlers for the parameters
554
555        '''
556
557        assert isinstance(return_value, TypeHandler)
558        assert isinstance(parameters, list)
559        assert all([isinstance(param, Parameter) for param in parameters])
560
561        self.return_value = return_value
562        self.parameters = parameters
563
564        if error_return is None:
565            error_return = return_value.get_c_error_return()
566        self.error_return = error_return
567        self.declarations = DeclarationsScope()
568        self.before_call = CodeBlock(error_return, self.declarations)
569        self.after_call = CodeBlock(error_return, self.declarations,
570                                    predecessor=self.before_call)
571        self.build_params = BuildValueParameters()
572        self.parse_params = ParseTupleParameters()
573        self._generate_gil_code()
574
575    def set_error_return(self, error_return):
576        self.error_return = error_return
577        self.before_call.error_return = error_return
578        self.after_call.error_return = error_return
579
580    def reset_code_generation_state(self):
581        self.declarations.clear()
582        self.before_call.clear()
583        self.after_call.clear()
584        self.build_params.clear()
585        self.parse_params.clear()
586        self._generate_gil_code()
587
588    def _generate_gil_code(self):
589        if self.NO_GIL_LOCKING:
590            return
591        ## reverse wrappers are called from C/C++ code, when the Python GIL may not be held...
592        gil_state_var = self.declarations.declare_variable('PyGILState_STATE', '__py_gil_state')
593        self.before_call.write_code('%s = (PyEval_ThreadsInitialized() ? PyGILState_Ensure() : (PyGILState_STATE) 0);'
594                                    % gil_state_var)
595        self.before_call.add_cleanup_code('if (PyEval_ThreadsInitialized())\n'
596                                          '    PyGILState_Release(%s);' % gil_state_var)
597
598
599    def generate_python_call(self):
600        """Generates the code (into self.before_call) to call into
601        Python, storing the result in the variable 'py_retval'; should
602        also check for call error.
603        """
604        raise NotImplementedError
605
606    def generate(self, code_sink, wrapper_name, decl_modifiers=('static',),
607                 decl_post_modifiers=()):
608        """Generate the wrapper
609
610        :param code_sink: a CodeSink object that will receive the code
611        :param wrapper_name: C/C++ identifier of the function/method to generate
612        :param decl_modifiers: list of C/C++ declaration modifiers, e.g. 'static'
613        """
614        assert isinstance(decl_modifiers, (list, tuple))
615        assert all([isinstance(mod, string_types) for mod in decl_modifiers])
616
617        #import sys
618        #print("generate", self, file=sys.stderr)
619        py_retval = self.declarations.declare_variable('PyObject*', 'py_retval')
620        assert py_retval == "py_retval", "py_retval already declared: "\
621          "generating the same wrapper twice without a reset() in between?"
622
623        if self.return_value.ctype != 'void' \
624                and not self.return_value.REQUIRES_ASSIGNMENT_CONSTRUCTOR \
625                and not self.return_value.NO_RETVAL_DECL:
626            self.declarations.declare_variable(self.return_value.ctype, 'retval')
627
628        ## convert the input parameters
629        for param in self.parameters:
630            param.convert_c_to_python(self)
631
632        ## generate_python_call should include something like
633        ## self.after_call.write_error_check('py_retval == NULL')
634        self.generate_python_call()
635
636        ## convert the return value(s)
637        self.return_value.convert_python_to_c(self)
638
639        if self.parse_params.is_empty():
640            self.before_call.write_error_check('py_retval != Py_None',
641                                               'PyErr_SetString(PyExc_TypeError, "function/method should return None");')
642        else:
643            ## parse the return value
644            ## this ensures that py_retval is always a tuple
645            self.before_call.write_code('py_retval = Py_BuildValue((char*) "(N)", py_retval);')
646
647            parse_tuple_params = ['py_retval']
648            parse_params = self.parse_params.get_parameters()
649            assert parse_params[0][0] == '"'
650            parse_params[0] = '(char *) ' + parse_params[0]
651            parse_tuple_params.extend(parse_params)
652            self.before_call.write_error_check('!PyArg_ParseTuple(%s)' %
653                                               (', '.join(parse_tuple_params),),
654                                               failure_cleanup='PyErr_Print();')
655
656        ## cleanup and return
657        self.after_call.write_cleanup()
658        if self.return_value.ctype == 'void':
659            self.after_call.write_code('return;')
660        else:
661            self.after_call.write_code('return retval;')
662
663        ## now write out the wrapper function itself
664
665        ## open function
666        retline = list(decl_modifiers)
667        retline.append(self.return_value.ctype)
668        code_sink.writeln(' '.join(retline))
669
670        params_list = ', '.join([join_ctype_and_name(param.ctype, param.name)
671                                 for param in self.parameters])
672        code_sink.writeln("%s(%s)%s" % (wrapper_name, params_list,
673                                        ' '.join([''] + list(decl_post_modifiers))))
674
675        ## body
676        code_sink.writeln('{')
677        code_sink.indent()
678        self.declarations.get_code_sink().flush_to(code_sink)
679        code_sink.writeln()
680        self.before_call.sink.flush_to(code_sink)
681        self.after_call.sink.flush_to(code_sink)
682
683        ## close function
684        code_sink.unindent()
685        code_sink.writeln('}')
686
687
688
689class ForwardWrapperBase(object):
690    """Generic base for all forward wrapper generators.
691
692    Forward wrappers all have the following general structure in common:
693
694     1. 'declarations' -- variable declarations; for compatibility
695            with older C compilers it is very important that all
696            declarations come before any simple statement.
697            Declarations can be added with the add_declaration()
698            method on the 'declarations' attribute.  Two standard
699            declarations are always predeclared: '<return-type>
700            retval', unless return-type is void, and 'PyObject
701            \\*py_retval';
702
703     2. 'code before parse' -- code before the
704         PyArg_ParseTupleAndKeywords call; code can be freely added to
705         it by accessing the 'before_parse' (a CodeBlock instance)
706         attribute;
707
708     3. A PyArg_ParseTupleAndKeywords call; uses items from the
709         parse_params object;
710
711     4. 'code before call' -- this is a code block dedicated to contain
712         all code that is needed before calling the C function; code can be
713         freely added to it by accessing the 'before_call' (a CodeBlock
714         instance) attribute;
715
716     5. 'call into C' -- this is realized by a C/C++ call; the list of
717         parameters that should be used is in the 'call_params' wrapper
718         attribute;
719
720     6. 'code after call' -- this is a code block dedicated to contain
721         all code that must come after calling into Python; code can be
722         freely added to it by accessing the 'after_call' (a CodeBlock
723         instance) attribute;
724
725     7. A py_retval = Py_BuildValue(...) call; this call can be
726        customized, so that out/inout parameters can add additional
727        return values, by accessing the 'build_params' (a
728        BuildValueParameters instance) attribute;
729
730     8. Cleanup and return.
731
732    Object constructors cannot return values, and so the step 7 is to
733    be omitted for them.
734
735    """
736
737    PARSE_TUPLE = 1
738    PARSE_TUPLE_AND_KEYWORDS = 2
739
740    HAVE_RETURN_VALUE = False # change to true if the wrapper
741                              # generates a return value even if the
742                              # return_value attribute is None
743
744    def __init__(self, return_value, parameters,
745                 parse_error_return, error_return,
746                 force_parse=None, no_c_retval=False,
747                 unblock_threads=False):
748        '''
749        Base constructor
750
751        :param return_value: type handler for the return value
752        :param parameters: a list of type handlers for the parameters
753        :param parse_error_return: statement to return an error during parameter parsing
754        :param error_return: statement to return an error after parameter parsing
755        :param force_parse: force generation of code to parse parameters even if there are none
756        :param no_c_retval: force the wrapper to not have a C return value
757        :param unblock_threads: generate code to unblock python threads during the C function call
758        '''
759        assert isinstance(return_value, ReturnValue) or return_value is None
760        assert isinstance(parameters, list)
761        assert all([isinstance(param, Parameter) for param in parameters])
762
763        self.return_value = return_value
764        self.parameters = parameters
765        self.declarations = DeclarationsScope()
766        self.before_parse = CodeBlock(parse_error_return, self.declarations)
767        self.before_call = CodeBlock(parse_error_return, self.declarations,
768                                     predecessor=self.before_parse)
769        self.after_call = CodeBlock(error_return, self.declarations,
770                                    predecessor=self.before_call)
771        self.build_params = BuildValueParameters()
772        self.parse_params = ParseTupleParameters()
773        self.call_params = []
774        self.force_parse = force_parse
775        self.meth_flags = []
776        self.unblock_threads = unblock_threads
777        self.no_c_retval = no_c_retval
778        self.overload_index = None
779        self.deprecated = False
780
781        # The following 3 variables describe the C wrapper function
782        # prototype; do not confuse with the python function/method!
783        self.wrapper_actual_name = None # name of the wrapper function/method
784        self.wrapper_return = None # C type expression for the wrapper return
785        self.wrapper_args = None # list of arguments to the wrapper function
786
787        self._init_code_generation_state()
788
789    def _init_code_generation_state(self):
790        if self.return_value is not None or self.HAVE_RETURN_VALUE:
791            self.declarations.declare_variable('PyObject*', 'py_retval')
792        if (not self.no_c_retval and (self.return_value is not None or self.HAVE_RETURN_VALUE)
793            and self.return_value.ctype != 'void'
794            and not self.return_value.REQUIRES_ASSIGNMENT_CONSTRUCTOR
795            and not self.return_value.NO_RETVAL_DECL):
796            self.declarations.declare_variable(str(self.return_value.type_traits.ctype_no_const_no_ref), 'retval')
797
798        self.declarations.reserve_variable('args')
799        self.declarations.reserve_variable('kwargs')
800
801    def reset_code_generation_state(self):
802        self.declarations.clear()
803        self.before_parse.clear()
804        self.before_call.clear()
805        self.after_call.clear()
806        self.build_params.clear()
807        self.parse_params.clear()
808        self.call_params = []
809        self.meth_flags = []
810
811        self._init_code_generation_state()
812
813    def set_parse_error_return(self, parse_error_return):
814        self.before_parse.error_return = parse_error_return
815        self.before_call.error_return = parse_error_return
816
817    def generate_call(self):
818        """Generates the code (into self.before_call) to call into
819        Python, storing the result in the variable 'py_retval'; should
820        also check for call error.
821        """
822        raise NotImplementedError
823
824    def _before_call_hook(self):
825        """
826        Optional hook that lets subclasses add code after all
827        parameters are parsed, but before the C function/method call.
828        Subclasses may add code to self.before_call.
829        """
830        pass
831
832    def _before_return_hook(self):
833        """
834        Optional hook that lets subclasses add code after all
835        parameters are parsed and after the function C return value is
836        processed, but after the python wrapper return value (py_ret)
837        is built and returned.  Subclasses may add code to
838        self.after_call, which will be placed before py_ret is
839        created.
840        """
841        pass
842
843    def write_open_wrapper(self, code_sink, add_static=False):
844        assert self.wrapper_actual_name is not None
845        assert self.wrapper_return is not None
846        assert isinstance(self.wrapper_args, list)
847        if add_static:
848            code_sink.writeln("static " + self.wrapper_return)
849        else:
850            code_sink.writeln(self.wrapper_return)
851        code_sink.writeln("%s(%s)" % (self.wrapper_actual_name, ', '.join(self.wrapper_args)))
852        code_sink.writeln('{')
853        code_sink.indent()
854
855    def write_close_wrapper(self, code_sink):
856        code_sink.unindent()
857        code_sink.writeln('}')
858
859
860    def generate_body(self, code_sink, gen_call_params=()):
861        """Generate the wrapper function body
862        code_sink -- a CodeSink object that will receive the code
863        """
864
865        if self.unblock_threads:
866            py_thread_state = self.declarations.declare_variable("PyThreadState*", "py_thread_state", "NULL")
867            self.after_call.write_code(
868                "\nif (%s)\n"
869                "     PyEval_RestoreThread(%s);\n" % (py_thread_state, py_thread_state))
870
871        ## convert the input parameters
872        for param in self.parameters:
873            try:
874                param.convert_python_to_c(self)
875            except NotImplementedError:
876                raise CodeGenerationError(
877                    'convert_python_to_c method of parameter %s not implemented'
878                    % (param.ctype,))
879
880        if self.deprecated:
881            if isinstance(self.deprecated, string_types):
882                msg = self.deprecated
883            else:
884                msg = "Deprecated"
885            self.before_call.write_error_check( 'PyErr_Warn(PyExc_DeprecationWarning, (char *) "%s")' % msg)
886
887        self._before_call_hook()
888
889        if self.unblock_threads:
890            self.before_call.write_code(
891                "\nif (PyEval_ThreadsInitialized ())\n"
892                "     %s = PyEval_SaveThread();\n"
893                % (py_thread_state, ))
894
895        self.generate_call(*gen_call_params)
896
897        params = self.parse_params.get_parameters()
898        assert params[0][0] == '"'
899        params_empty = (params == ['""'])
900        params[0] = '(char *) ' + params[0]
901        keywords = self.parse_params.get_keywords()
902        if not params_empty or self.force_parse != None:
903            self.meth_flags.append("METH_VARARGS")
904            if keywords is None \
905                    and self.force_parse != self.PARSE_TUPLE_AND_KEYWORDS:
906
907                param_list = ['args'] + params
908                self.before_parse.write_error_check('!PyArg_ParseTuple(%s)' %
909                                                    (', '.join(param_list),))
910            else:
911                if keywords is None:
912                    keywords = []
913                keywords_var = self.declarations.declare_variable(
914                    'const char *', 'keywords',
915                    '{' + ', '.join(['"%s"' % kw for kw in keywords] + ['NULL']) + '}',
916                     '[]')
917                param_list = ['args', 'kwargs', params[0], '(char **) ' + keywords_var] + params[1:]
918                self.before_parse.write_error_check('!PyArg_ParseTupleAndKeywords(%s)' %
919                                                    (', '.join(param_list),))
920                self.meth_flags.append("METH_KEYWORDS")
921        else:
922            self.meth_flags.append("METH_NOARGS")
923
924        ## convert the return value(s)
925        if self.return_value is None and not self.HAVE_RETURN_VALUE:
926
927            assert self.build_params.get_parameters() == ['""'], \
928                   "this wrapper is not supposed to return values"
929            self._before_return_hook()
930            self.after_call.write_cleanup()
931
932        else:
933
934            if self.return_value is not None:
935                try:
936                    self.return_value.convert_c_to_python(self)
937                except NotImplementedError:
938                    raise CodeGenerationError(
939                        'convert_c_to_python method of return value %s not implemented'
940                        % (self.return_value.ctype,))
941
942            self._before_return_hook()
943
944            params = self.build_params.get_parameters()
945            if params:
946                if params == ['""']:
947                    self.after_call.write_code('Py_INCREF(Py_None);')
948                    self.after_call.write_code('py_retval = Py_None;')
949                else:
950                    assert params[0][0] == '"'
951                    params[0] = "(char *) " + params[0]
952                    self.after_call.write_code('py_retval = Py_BuildValue(%s);' %
953                                               (', '.join(params),))
954
955            ## cleanup and return
956            self.after_call.write_cleanup()
957            self.after_call.write_code('return py_retval;')
958
959        ## now write out the wrapper function body itself
960        self.declarations.get_code_sink().flush_to(code_sink)
961        code_sink.writeln()
962        self.before_parse.sink.flush_to(code_sink)
963        self.before_call.sink.flush_to(code_sink)
964        self.after_call.sink.flush_to(code_sink)
965
966    def get_py_method_def_flags(self):
967        """
968        Get a list of PyMethodDef flags that should be used for this wrapper.
969        """
970        flags = set(self.meth_flags)
971        if flags:
972            return list(flags)
973
974        tmp_sink = codesink.NullCodeSink()
975        try:
976#             try:
977#                 self.generate_body(tmp_sink)
978#             except CodegenErrorBase:
979#                 return []
980#             else:
981#                 return list(set(self.meth_flags))
982            self.generate_body(tmp_sink)
983            return list(set(self.meth_flags))
984        finally:
985            self.reset_code_generation_state()
986
987
988class TypeTransformation(object):
989    """
990    Type transformations are used to register handling of special
991    types that are simple transformation over another type that is
992    already registered.  This way, only the original type is
993    registered, and the type transformation only does the necessary
994    adjustments over the original type handler to make it handle the
995    transformed type as well.
996
997    This is typically used to get smart pointer templated types
998    working.
999
1000    """
1001
1002    def get_untransformed_name(self, name):
1003        """
1004        Given a transformed named, get the original C type name.
1005        E.g., given a smart pointer transformation, MySmartPointer::
1006
1007          get_untransformed_name('MySmartPointer<Foo>') -> 'Foo\\*'
1008        """
1009        raise NotImplementedError
1010
1011    def create_type_handler(self, type_handler_class, *args, **kwargs):
1012        """
1013        Given a type_handler class, create an instance with proper customization.
1014
1015        :param type_handler_class: type handler class
1016        :param args: arguments
1017        :param kwargs: keywords arguments
1018        """
1019        raise NotImplementedError
1020
1021    def transform(self, type_handler, declarations, code_block, value):
1022        """
1023        Transforms a value expression of the original type to an
1024        equivalent value expression in the transformed type.
1025
1026        Example, with the transformation::
1027           'T\\*' -> 'boost::shared_ptr<T>'
1028        Then::
1029           transform(wrapper, 'foo') -> 'boost::shared_ptr<%s>(foo)' % type_handler.untransformed_ctype
1030        """
1031        raise NotImplementedError
1032
1033    def untransform(self, type_handler, declarations, code_block, value):
1034        """
1035        Transforms a value expression of the transformed type to an
1036        equivalent value expression in the original type.
1037
1038        Example, with the transformation::
1039          'T\\*' -> 'boost::shared_ptr<T>'
1040        Then::
1041           untransform(wrapper, 'foo') -> 'foo->get_pointer()'
1042        """
1043        raise NotImplementedError
1044
1045
1046class NullTypeTransformation(object):
1047    """
1048    Null type transformation, returns everything unchanged.
1049    """
1050    def get_untransformed_name(self, name):
1051        "identity transformation"
1052        return name
1053    def create_type_handler(self, type_handler_class, *args, **kwargs):
1054        "identity transformation"
1055        return type_handler_class(*args, **kwargs)
1056    def transform(self, type_handler, declarations, code_block, value):
1057        "identity transformation"
1058        return value
1059    def untransform(self, type_handler, declarations, code_block, value):
1060        "identity transformation"
1061        return value
1062
1063class TypeHandler(object):
1064    SUPPORTS_TRANSFORMATIONS = False
1065
1066    def __init__(self, ctype, is_const=False):
1067        if ctype is None:
1068            self.ctype = None
1069            self.untransformed_ctype = None
1070            self.type_traits = None
1071        else:
1072            if isinstance(ctype, ctypeparser.TypeTraits):
1073                self.type_traits = ctype
1074                if is_const:
1075                    warnings.warn("is_const is deprecated, put a 'const' in the C type instead.", DeprecationWarning)
1076                    if self.type_traits.type_is_pointer or self.type_traits.type_is_reference:
1077                        self.type_traits.make_target_const()
1078                    else:
1079                        self.type_traits.make_const()
1080            elif isinstance(ctype, string_types):
1081                if is_const:
1082                    warnings.warn("is_const is deprecated, put a 'const' in the C type instead.", DeprecationWarning)
1083                    self.type_traits = ctypeparser.TypeTraits('const %s' % ctype)
1084                else:
1085                    self.type_traits = ctypeparser.TypeTraits(ctype)
1086            else:
1087                raise TypeError
1088            self.ctype = str(self.type_traits.ctype)
1089        self.untransformed_ctype = self.ctype
1090        self.transformation = NullTypeTransformation()
1091
1092    def _get_ctype_no_const(self):
1093        return str(self.type_traits.ctype_no_const)
1094    ctype_no_const = property(_get_ctype_no_const)
1095
1096    def set_tranformation(self, transformation, untransformed_ctype):
1097        warnings.warn("Typo: set_tranformation -> set_transformation", DeprecationWarning, stacklevel=2)
1098        return self.set_transformation(transformation, untransformed_ctype)
1099
1100    def set_transformation(self, transformation, untransformed_ctype):
1101        "Set the type transformation to use in this type handler"
1102
1103        assert isinstance(transformation, TypeTransformation)
1104        assert untransformed_ctype != self.ctype
1105        assert isinstance(self.transformation, NullTypeTransformation)
1106        assert self.SUPPORTS_TRANSFORMATIONS
1107
1108        self.transformation = transformation
1109        self.untransformed_ctype = untransformed_ctype
1110
1111
1112
1113class ReturnValueMeta(type):
1114    "Metaclass for automatically registering parameter type handlers"
1115    def __init__(mcs, name, bases, dict_):
1116        "metaclass __init__"
1117        type.__init__(mcs, name, bases, dict_)
1118        if __debug__:
1119            try:
1120                iter(mcs.CTYPES)
1121            except (TypeError, AttributeError):
1122                sys.stderr.write("ERROR: missing CTYPES on class %s.%s\n" % (mcs.__module__, mcs.__name__))
1123
1124        for ctype in mcs.CTYPES:
1125            return_type_matcher.register(ctype, mcs)
1126
1127
1128class _ReturnValue(TypeHandler):
1129    '''Abstract base class for all classes dedicated to handle
1130    specific return value types'''
1131
1132    ## list of C type names it can handle
1133    CTYPES = []
1134
1135    ## whether it supports type transformations
1136    SUPPORTS_TRANSFORMATIONS = False
1137
1138    REQUIRES_ASSIGNMENT_CONSTRUCTOR = False
1139    NO_RETVAL_DECL = False
1140
1141    #@classmethod
1142    def new(cls, *args, **kwargs):
1143        """
1144        >>> import inttype
1145        >>> isinstance(ReturnValue.new('int'), inttype.IntReturn)
1146        True
1147        """
1148        if cls is ReturnValue:
1149            ctype = args[0]
1150            type_handler_class, transformation, type_traits = \
1151                return_type_matcher.lookup(ctype)
1152            assert type_handler_class is not None
1153            if transformation is None:
1154                args = list(args)
1155                args[0] = type_traits
1156                args = tuple(args)
1157                try:
1158                    return type_handler_class(*args, **kwargs)
1159                except TypeError:
1160                    ex = sys.exc_info()[1]
1161                    warnings.warn("Exception %r in type handler %s constructor" % (str(ex), type_handler_class))
1162                    raise
1163            else:
1164                return transformation.create_type_handler(type_handler_class, *args, **kwargs)
1165        else:
1166            return cls(*args, **kwargs)
1167
1168    new = classmethod(new)
1169
1170    def __init__(self, ctype, is_const=False):
1171        '''
1172        Creates a return value object
1173
1174        Keywork Arguments:
1175
1176        :param ctype: actual C/C++ type being used
1177        '''
1178        if type(self) is ReturnValue:
1179            raise TypeError('ReturnValue is an abstract class; use ReturnValue.new(...)')
1180        super(_ReturnValue, self).__init__(ctype, is_const)
1181        self.value = 'retval'
1182
1183    def get_c_error_return(self):
1184        '''Return a "return <value>" code string, for use in case of error'''
1185        raise NotImplementedError
1186
1187    def convert_python_to_c(self, wrapper):
1188        '''
1189        Writes code to convert the Python return value into the C "retval" variable.
1190        '''
1191        raise NotImplementedError
1192        #assert isinstance(wrapper, ReverseWrapperBase)
1193
1194    def convert_c_to_python(self, wrapper):
1195        '''
1196        Writes code to convert the C return value the Python return.
1197        '''
1198        raise NotImplementedError
1199        #assert isinstance(wrapper, ReverseWrapperBase)
1200
1201if PY3:
1202    ReturnValue = types.new_class("ReturnValue", (_ReturnValue,), dict(metaclass=ReturnValueMeta))
1203else:
1204    class ReturnValue(_ReturnValue):
1205        __metaclass__ = ReturnValueMeta
1206
1207ReturnValue.CTYPES = NotImplemented
1208
1209
1210
1211
1212class PointerReturnValue(ReturnValue):
1213    """Base class for all pointer-to-something handlers"""
1214    CTYPES = []
1215    def __init__(self, ctype, is_const=False, caller_owns_return=None, free_after_copy=None):
1216        super(PointerReturnValue, self).__init__(ctype, is_const)
1217        self.call_owns_return = caller_owns_return
1218        self.free_after_copy = free_after_copy
1219
1220PointerReturnValue.CTYPES = NotImplemented
1221
1222
1223class ParameterMeta(type):
1224    "Metaclass for automatically registering parameter type handlers"
1225    def __init__(mcs, name, bases, dict_):
1226        "metaclass __init__"
1227        type.__init__(mcs, name, bases, dict_)
1228        if __debug__:
1229            try:
1230                iter(mcs.CTYPES)
1231            except TypeError:
1232                sys.stderr.write("ERROR: missing CTYPES on class %s\n" % mcs)
1233        for ctype in mcs.CTYPES:
1234            param_type_matcher.register(ctype, mcs)
1235
1236
1237class _Parameter(TypeHandler):
1238    '''Abstract base class for all classes dedicated to handle specific parameter types'''
1239
1240    ## bit mask values
1241    DIRECTION_IN = 1
1242    DIRECTION_OUT = 2
1243    DIRECTION_INOUT = DIRECTION_IN|DIRECTION_OUT
1244
1245    ## list of possible directions for this type
1246    DIRECTIONS = NotImplemented
1247    ## whether it supports type transformations
1248    SUPPORTS_TRANSFORMATIONS = False
1249    ## list of C type names it can handle
1250    CTYPES = []
1251
1252    def _direction_value_to_name(cls, value):
1253        if value == cls.DIRECTION_IN:
1254            return "DIRECTION_IN"
1255        elif value == cls.DIRECTION_OUT:
1256            return "DIRECTION_OUT"
1257        elif value == cls.DIRECTION_INOUT:
1258            return "DIRECTION_INOUT"
1259        else:
1260            return "(invalid %r)" % value
1261    _direction_value_to_name = classmethod(_direction_value_to_name)
1262
1263
1264    #@classmethod
1265    def new(cls, *args, **kwargs):
1266        """
1267        >>> import inttype
1268        >>> isinstance(Parameter.new('int', 'name'), inttype.IntParam)
1269        True
1270        """
1271        if cls is Parameter:
1272            # support calling Parameter("typename", ...)
1273            ctype = args[0]
1274            type_handler_class, transformation, type_traits = \
1275                param_type_matcher.lookup(ctype)
1276            assert type_handler_class is not None
1277            if transformation is None:
1278                args = list(args)
1279                args[0] = type_traits
1280                args = tuple(args)
1281                try:
1282                    return type_handler_class(*args, **kwargs)
1283                except TypeError:
1284                    _, ex, _ = sys.exc_info()
1285                    warnings.warn("Exception %r in type handler %s constructor" % (str(ex), type_handler_class))
1286                    raise
1287            else:
1288                return transformation.create_type_handler(type_handler_class, *args, **kwargs)
1289        else:
1290            return cls(*args, **kwargs)
1291
1292    new = classmethod(new)
1293
1294    def __init__(self, ctype, name, direction=DIRECTION_IN, is_const=False, default_value=None):
1295        '''
1296        Creates a parameter object
1297
1298        :param ctype: actual C/C++ type being used
1299        :param name: parameter name
1300        :param direction: direction of the parameter transfer, valid values
1301                     are DIRECTION_IN, DIRECTION_OUT, and
1302                     DIRECTION_IN|DIRECTION_OUT
1303        '''
1304        if type(self) is Parameter:
1305            raise TypeError('Parameter is an abstract class; use Parameter.new(...)')
1306        super(_Parameter, self).__init__(ctype, is_const)
1307        self.name = name
1308        assert direction in self.DIRECTIONS, \
1309            "Error: requested direction %s for type handler %r (ctype=%r), but it only supports directions %r"\
1310            % (self._direction_value_to_name(direction), type(self), self.ctype,
1311               [self._direction_value_to_name(d) for d in self.DIRECTIONS])
1312        self.direction = direction
1313        self.value = name
1314        self.default_value = default_value
1315
1316    def convert_c_to_python(self, wrapper):
1317        '''Write some code before calling the Python method.'''
1318        #assert isinstance(wrapper, ReverseWrapperBase)
1319        raise NotImplementedError
1320
1321    def convert_python_to_c(self, wrapper):
1322        '''Write some code before calling the C method.'''
1323        #assert isinstance(wrapper, ReverseWrapperBase)
1324        raise NotImplementedError
1325
1326if PY3:
1327    Parameter = types.new_class("Parameter", (_Parameter,), dict(metaclass=ParameterMeta))
1328else:
1329    class Parameter(_Parameter):
1330        __metaclass__ = ParameterMeta
1331
1332
1333Parameter.CTYPES = NotImplemented
1334
1335class PointerParameter(Parameter):
1336    """Base class for all pointer-to-something handlers"""
1337
1338    CTYPES = []
1339
1340    def __init__(self, ctype, name, direction=Parameter.DIRECTION_IN, is_const=False, default_value=None,
1341                 transfer_ownership=False, free_after_copy=False):
1342        super(PointerParameter, self).__init__(ctype, name, direction, is_const, default_value)
1343        self.transfer_ownership = transfer_ownership
1344        self.free_after_copy = free_after_copy
1345
1346PointerParameter.CTYPES = NotImplemented
1347
1348
1349class TypeMatcher(object):
1350    """
1351    Type matcher object: maps C type names to classes that handle
1352    those types.
1353    """
1354
1355    def __init__(self):
1356        """Constructor"""
1357        self._types = {}
1358        self._transformations = []
1359        self._type_aliases = {}
1360        self._type_aliases_rev = {}
1361
1362
1363    def register_transformation(self, transformation):
1364        "Register a type transformation object"
1365        assert isinstance(transformation, TypeTransformation)
1366        self._transformations.append(transformation)
1367
1368    def register(self, name, type_handler):
1369        """Register a new handler class for a given C type
1370
1371        :param name: C type name
1372
1373        :param type_handler: class to handle this C type
1374        """
1375        name = ctypeparser.normalize_type_string(name)
1376        if name in self._types:
1377            raise ValueError("return type %s already registered" % (name,))
1378        self._types[name] = type_handler
1379
1380    def _raw_lookup_with_alias_support(self, name):
1381        already_tried = []
1382        return self._raw_lookup_with_alias_support_recursive(name, already_tried)
1383
1384    def _raw_lookup_with_alias_support_recursive(self, name, already_tried):
1385        try:
1386            return self._types[name]
1387        except KeyError:
1388            aliases_to_try = []
1389            try:
1390                aliases_to_try.append(self._type_aliases[name])
1391            except KeyError:
1392                pass
1393            try:
1394                aliases_to_try.append(self._type_aliases_rev[name])
1395            except KeyError:
1396                pass
1397            for alias in aliases_to_try:
1398                if alias in already_tried:
1399                    continue
1400                already_tried.append(name)
1401                #if 'Time' in name or 'Time' in alias:
1402                #    import sys
1403                #    print >> sys.stderr, "**** trying name %r in place of %r" % (alias, name)
1404                return self._raw_lookup_with_alias_support_recursive(alias, already_tried)
1405            raise KeyError
1406
1407    def lookup(self, name):
1408        """
1409        lookup(name) -> type_handler, type_transformation, type_traits
1410
1411        :param name: C type name, possibly transformed (e.g. MySmartPointer<Foo> looks up Foo*)
1412        :returns: a handler with the given ctype name, or raises KeyError.
1413
1414        Supports type transformations.
1415
1416        """
1417        logger.debug("TypeMatcher.lookup(%r)", name)
1418        given_type_traits = ctypeparser.TypeTraits(name)
1419        noconst_name = str(given_type_traits.ctype_no_modifiers)
1420        tried_names = [noconst_name]
1421        try:
1422            rv = self._raw_lookup_with_alias_support(noconst_name), None, given_type_traits
1423        except KeyError:
1424            logger.debug("try to lookup type handler for %r => failure", name)
1425            ## Now try all the type transformations
1426            for transf in self._transformations:
1427                untransformed_name = transf.get_untransformed_name(name)
1428                if untransformed_name is None:
1429                    continue
1430                untransformed_type_traits = ctypeparser.TypeTraits(untransformed_name)
1431                untransformed_name = str(untransformed_type_traits.ctype_no_modifiers)
1432                try:
1433                    rv = self._raw_lookup_with_alias_support(untransformed_name), transf, untransformed_type_traits
1434                except KeyError as ex:
1435                    logger.debug("try to lookup type handler for %r => failure (%r)", untransformed_name, str(ex))
1436                    tried_names.append(untransformed_name)
1437                    continue
1438                else:
1439                    logger.debug("try to lookup type handler for %r => success (%r)", untransformed_name, rv)
1440                    return rv
1441            else:
1442                #if 'Time' in name:
1443                #    existing = [k for k in self._types.iterkeys() if 'Time' in k]
1444                #    existing.sort()
1445                #    raise TypeLookupError((tried_names, existing, self._type_aliases))
1446                #else:
1447                raise TypeLookupError(tried_names)
1448        else:
1449            logger.debug("try to lookup type handler for %r => success (%r)", name, rv)
1450            return rv
1451
1452    def items(self):
1453        "Returns an iterator over all registered items"
1454        return iter(self._types.items())
1455
1456    def add_type_alias(self, from_type_name, to_type_name):
1457        from_type_name_normalized = str(ctypeparser.TypeTraits(from_type_name).ctype)
1458        to_type_name_normalized = str(ctypeparser.TypeTraits(to_type_name).ctype)
1459        self._type_aliases[to_type_name_normalized] = from_type_name_normalized
1460        self._type_aliases_rev[from_type_name_normalized] = to_type_name_normalized
1461
1462return_type_matcher = TypeMatcher()
1463param_type_matcher = TypeMatcher()
1464
1465def add_type_alias(from_type_name, to_type_name):
1466    return_type_matcher.add_type_alias(from_type_name, to_type_name)
1467    param_type_matcher.add_type_alias(from_type_name, to_type_name)
1468
1469