1# Copyright 2004-2008 Roman Yakovenko.
2# Distributed under the Boost Software License, Version 1.0. (See
3# accompanying file LICENSE_1_0.txt or copy at
4# http://www.boost.org/LICENSE_1_0.txt)
5
6"""Contains definition of call policies classes"""
7
8from . import algorithm
9from . import python_traits
10from pygccxml import declarations
11
12#keeps file name, where `Py++` defined call policies will be defined
13PYPP_CALL_POLICIES_HEADER_FILE = "__call_policies.pypp.hpp"
14
15class CREATION_POLICY:
16    """Implementation details"""
17    AS_INSTANCE = 'as instance'
18    AS_TEMPLATE_ARGUMENT = 'as template argument'
19
20class call_policy_t(object):
21    """base class for all classes, which generate "call policies" code"""
22    def __init__(self):
23        object.__init__(self)
24
25    def create(self, function_creator, creation_policy=CREATION_POLICY.AS_INSTANCE):
26        """Creates code from the call policies class instance.
27        :param function_creator: parent code creator
28        :type function_creator: :class:`code_creators.function_t` or :class:`code_creators.constructor_t`
29
30        :param creation_policy: indicates whether we this call policy used as template
31                                argument or as an instance
32        :type creation_policy: :class:`decl_wrappers.CREATION_POLICY`
33        """
34        code = self._create_impl( function_creator )
35        if code and creation_policy == CREATION_POLICY.AS_INSTANCE:
36            code = code + '()'
37        return code
38
39    def create_type(self):
40        """return call policies class declaration as string"""
41        return self.create( None, CREATION_POLICY.AS_TEMPLATE_ARGUMENT )
42
43    def create_template_arg( self, function_creator ):
44        """return call policies class declaration as string"""
45        return self.create( function_creator, CREATION_POLICY.AS_TEMPLATE_ARGUMENT )
46
47    def is_default( self ):
48        """return True is self is instance of :class:`decl_wrappers.default_call_policies_t` class"""
49        return False
50
51    def is_predefined( self ):
52        """return True if call policy is defined in Boost.Python library, False otherwise"""
53        return True
54
55    def _create_impl( self, function_creator ):
56        raise NotImplementedError()
57
58    @property
59    def header_file(self):
60        """return a name of the header file the call policy is defined in"""
61        return "boost/python.hpp"
62
63class default_call_policies_t(call_policy_t):
64    """implements code generation for boost::python::default_call_policies"""
65    def __init__( self ):
66        call_policy_t.__init__( self )
67
68    def _create_impl(self, function_creator ):
69        return algorithm.create_identifier( function_creator, '::boost::python::default_call_policies' )
70
71    def is_default( self ):
72        return True
73
74    def __str__(self):
75        return 'default_call_policies'
76
77def default_call_policies():
78    """create ::boost::python::default_call_policies call policies code generator"""
79    return default_call_policies_t()
80
81class compound_policy_t( call_policy_t ):
82    """base class for all call policies, except the default one"""
83    def __init__( self, base=None ):
84        call_policy_t.__init__( self )
85        self._base = base
86        if not base:
87            self._base = default_call_policies_t()
88
89    def _get_base_policy( self ):
90        return self._base
91    def _set_base_policy( self, new_policy ):
92        self._base = new_policy
93    base_policy = property( _get_base_policy, _set_base_policy
94                            , doc="base call policy, by default is reference to :class:`decl_wrappers.default_call_policies_t` call policy")
95
96    def _get_args(self, function_creator):
97        return []
98
99    def _get_name(self, function_creator):
100        raise NotImplementedError()
101
102    def _create_impl( self, function_creator ):
103        args = self._get_args(function_creator)
104        if not self._base.is_default():
105            args.append( self._base.create( function_creator, CREATION_POLICY.AS_TEMPLATE_ARGUMENT ) )
106        name = algorithm.create_identifier( function_creator, self._get_name(function_creator) )
107        return declarations.templates.join( name, args )
108
109    def __str__(self):
110        name = self._get_name(None).replace('::boost::python::', '' )
111        args = [text.replace( '::boost::python::', '' ) for text in self._get_args( None )]
112        return declarations.templates.join( name, args )
113
114class return_argument_t( compound_policy_t ):
115    """implements code generation for boost::python::return_argument call policies"""
116    def __init__( self, position=1, base=None):
117        compound_policy_t.__init__( self, base )
118        self._position = position
119
120    def _get_position( self ):
121        return self._position
122    def _set_position( self, new_position):
123        self._position = new_position
124    position = property( _get_position, _set_position )
125
126    def _get_name(self, function_creator):
127        if self.position == 1:
128            return '::boost::python::return_self'
129        else:
130            return '::boost::python::return_arg'
131
132    def _get_args(self, function_creator):
133        if self.position == 1:
134            return []
135        else:
136            return [ str( self.position ) ]
137
138def return_arg( arg_pos, base=None ):
139    """create boost::python::return_arg call policies code generator"""
140    return return_argument_t( arg_pos, base )
141
142def return_self(base=None):
143    """create boost::python::return_self call policies code generator"""
144    return return_argument_t( 1, base )
145
146class return_internal_reference_t( compound_policy_t ):
147    """implements code generation for boost::python::return_internal_reference call policies"""
148    def __init__( self, position=1, base=None):
149        compound_policy_t.__init__( self, base )
150        self._position = position
151
152    def _get_position( self ):
153        return self._position
154    def _set_position( self, new_position):
155        self._position = new_position
156    position = property( _get_position, _set_position )
157
158    def _get_name(self, function_creator):
159        return '::boost::python::return_internal_reference'
160
161    def _get_args(self, function_creator):
162        if self.position == 1:
163            return [] #don't generate default template arguments
164        else:
165            return [ str( self.position ) ]
166
167def return_internal_reference( arg_pos=1, base=None):
168    """create boost::python::return_internal_reference call policies code generator"""
169    return return_internal_reference_t( arg_pos, base )
170
171class with_custodian_and_ward_t( compound_policy_t ):
172    """implements code generation for boost::python::with_custodian_and_ward call policies"""
173    def __init__( self, custodian, ward, base=None):
174        compound_policy_t.__init__( self, base )
175        self._custodian = custodian
176        self._ward = ward
177
178    def _get_custodian( self ):
179        return self._custodian
180    def _set_custodian( self, new_custodian):
181        self._custodian = new_custodian
182    custodian = property( _get_custodian, _set_custodian )
183
184    def _get_ward( self ):
185        return self._ward
186    def _set_ward( self, new_ward):
187        self._ward = new_ward
188    ward = property( _get_ward, _set_ward )
189
190    def _get_name(self, function_creator):
191        return '::boost::python::with_custodian_and_ward'
192
193    def _get_args(self, function_creator):
194        return [ str( self.custodian ), str( self.ward ) ]
195
196def with_custodian_and_ward( custodian, ward, base=None):
197    """create boost::python::with_custodian_and_ward call policies code generator"""
198    return with_custodian_and_ward_t( custodian, ward, base )
199
200class with_custodian_and_ward_postcall_t( with_custodian_and_ward_t ):
201    """implements code generation for boost::python::with_custodian_and_ward_postcall call policies"""
202    def __init__( self, custodian, ward, base=None):
203        with_custodian_and_ward_t.__init__( self, custodian, ward, base )
204
205    def _get_name(self, function_creator):
206        return '::boost::python::with_custodian_and_ward_postcall'
207
208def with_custodian_and_ward_postcall( custodian, ward, base=None):
209    """create boost::python::with_custodian_and_ward_postcall call policies code generator"""
210    return with_custodian_and_ward_postcall_t( custodian, ward, base )
211
212class return_value_policy_t( compound_policy_t ):
213    """implements code generation for boost::python::return_value_policy call policies"""
214    def __init__( self, result_converter_generator, base=None):
215        compound_policy_t.__init__( self, base )
216        self._result_converter_generator = result_converter_generator
217
218    def _get_result_converter_generator( self ):
219        return self._result_converter_generator
220    def _set_result_converter_generator( self, new_result_converter_generator):
221        self._result_converter_generator = new_result_converter_generator
222    result_converter_generator = property( _get_result_converter_generator
223                                           , _set_result_converter_generator )
224
225    def _get_name(self, function_creator):
226        return '::boost::python::return_value_policy'
227
228    def _get_args(self, function_creator):
229        if function_creator:
230            rcg = algorithm.create_identifier( function_creator, self.result_converter_generator )
231            return [ rcg ]
232        else:
233            return [self.result_converter_generator]
234
235    def is_predefined( self ):
236        """Returns True if call policy is defined in Boost.Python library, False otherwise"""
237        global return_addressof
238        global return_pointee_value
239        if self.result_converter_generator in (return_pointee_value, return_addressof ):
240            return False
241        else:
242            return True
243
244    @property
245    def header_file(self):
246        """Return name of the header file to be included"""
247        if self.is_predefined():
248            return super( return_value_policy_t, self ).header_file
249        else:
250            return PYPP_CALL_POLICIES_HEADER_FILE
251
252
253copy_const_reference = '::boost::python::copy_const_reference'
254copy_non_const_reference = '::boost::python::copy_non_const_reference'
255manage_new_object = '::boost::python::manage_new_object'
256reference_existing_object = '::boost::python::reference_existing_object'
257return_by_value = '::boost::python::return_by_value'
258return_opaque_pointer = '::boost::python::return_opaque_pointer'
259return_pointee_value = '::pyplusplus::call_policies::return_pointee_value'
260return_addressof = '::pyplusplus::call_policies::return_addressof'
261
262def return_value_policy( result_converter_generator, base=None):
263    """create boost::python::return_value_policy call policies code generator"""
264    return return_value_policy_t( result_converter_generator, base )
265
266def is_return_opaque_pointer_policy( policy ):
267    """returns True is policy represents return_value_policy<return_opaque_pointer>, False otherwise"""
268    return isinstance( policy, return_value_policy_t ) \
269            and policy.result_converter_generator == return_opaque_pointer
270
271class custom_call_policies_t(call_policy_t):
272    """implements code generation for user defined call policies"""
273    def __init__( self, call_policies, header_file=None ):
274        call_policy_t.__init__( self )
275        self.__call_policies = call_policies
276        self.__header_file = header_file
277
278    def _create_impl(self, function_creator ):
279        return str( self.__call_policies )
280
281    def __str__(self):
282        return 'custom call policies'
283
284    def get_header_file( self ):
285        return self.__header_file
286    def set_header_file( self, header_file_name ):
287        self.__header_file = header_file_name
288    header_file = property( get_header_file, set_header_file
289                            , doc="""Return name of the header file to be included""" )
290
291def custom_call_policies(call_policies, header_file=None):
292    """create custom\\user defined call policies code generator"""
293    return custom_call_policies_t(call_policies, header_file)
294
295class memory_managers:
296    """implements code generation for `Py++` defined memory managers
297
298    For complete documentation and usage example see "Call policies" document.
299    """
300    none = 'none'
301    delete_ = 'delete_'
302    all = [ none, delete_ ]
303
304    @staticmethod
305    def create( manager, function_creator=None):
306        mem_manager = 'pyplusplus::call_policies::memory_managers::' + manager
307        if function_creator:
308            mem_manager = algorithm.create_identifier( function_creator, mem_manager )
309        return mem_manager
310
311class convert_array_to_tuple_t( compound_policy_t ):
312    """implements code generation for `Py++` defined "as_tuple" value policy
313
314    For complete documentation and usage example see "Call policies" document.
315    """
316    def __init__( self, array_size, memory_manager, make_object_call_policies=None, base=None):
317        compound_policy_t.__init__( self, base )
318        self._array_size = array_size
319        self._memory_manager = memory_manager
320        self._make_objec_call_policies = make_object_call_policies
321
322    def is_predefined( self ):
323        """Returns True if call policy is defined in Boost.Python library, False otherwise"""
324        return False
325
326    @property
327    def header_file(self):
328        """Return name of the header file to be included"""
329        return PYPP_CALL_POLICIES_HEADER_FILE
330
331    def _get_array_size( self ):
332        return self._array_size
333    def _set_array_size( self, new_array_size):
334        self._array_size = new_array_size
335    array_size = property( _get_array_size, _set_array_size )
336
337    def _get_memory_manager( self ):
338        return self._memory_manager
339    def _set_memory_manager( self, new_memory_manager):
340        self._memory_manager = new_memory_manager
341    memory_manager = property( _get_memory_manager, _set_memory_manager )
342
343    def _get_make_objec_call_policies( self ):
344        if None is self._make_objec_call_policies:
345            self._make_objec_call_policies = default_call_policies()
346        return self._make_objec_call_policies
347    def _set_make_objec_call_policies( self, new_make_objec_call_policies):
348        self._make_objec_call_policies = new_make_objec_call_policies
349    make_objec_call_policies = property( _get_make_objec_call_policies, _set_make_objec_call_policies )
350
351    def _get_name(self, function_creator):
352        return '::boost::python::return_value_policy'
353
354    def _get_args(self, function_creator):
355        as_tuple_args = [ str( self.array_size ) ]
356        as_tuple_args.append( memory_managers.create( self.memory_manager, function_creator ) )
357        if not self.make_objec_call_policies.is_default():
358            as_tuple_args.append( self.make_objec_call_policies.create_template_arg( function_creator ) )
359        as_tuple = '::pyplusplus::call_policies::arrays::as_tuple'
360        if function_creator:
361            as_tuple = algorithm.create_identifier( function_creator, as_tuple )
362        return [ declarations.templates.join( as_tuple, as_tuple_args ) ]
363
364def convert_array_to_tuple( array_size, memory_manager, make_object_call_policies=None, base=None ):
365    """create boost::python::return_value_policy< py++::as_tuple > call policies code generator"""
366    return convert_array_to_tuple_t( array_size, memory_manager, make_object_call_policies, base )
367
368class return_range_t( call_policy_t ):
369    """implements code generation for `Py++` defined "return_range" call policies
370
371    For complete documentation and usage example see "Call policies" document.
372    """
373    HEADER_FILE = "__return_range.pypp.hpp"
374    def __init__( self, get_size_class, value_type, value_policies):
375        call_policy_t.__init__( self )
376        self._value_type = value_type
377        self._get_size_class = get_size_class
378        self._value_policies = value_policies
379
380    def is_predefined( self ):
381        """Returns True if call policy is defined in Boost.Python library, False otherwise"""
382        return False
383
384    @property
385    def header_file(self):
386        """Return name of the header file to be included"""
387        return self.HEADER_FILE
388
389    def _get_get_size_class( self ):
390        return self._get_size_class
391    def _set_get_size_class( self, new_get_size_class):
392        self._get_size_class = new_get_size_class
393    get_size_class = property( _get_get_size_class, _set_get_size_class )
394
395    def _get_value_type( self ):
396        return self._value_type
397    def _set_value_type( self, new_value_type):
398        self._value_type = new_value_type
399    value_type = property( _get_value_type, _set_value_type )
400
401    def _get_value_policies( self ):
402        return self._value_policies
403    def _set_value_policies( self, new_value_policies):
404        self._value_policies = new_value_policies
405    value_policies = property( _get_value_policies, _set_value_policies )
406
407    def _create_impl(self, function_creator ):
408        name = algorithm.create_identifier( function_creator, '::pyplusplus::call_policies::return_range' )
409        args = [ self.get_size_class, self.value_type.decl_string ]
410        if not self.value_policies.is_default():
411            args.append( self.value_policies.create_type() )
412        return declarations.templates.join( name, args )
413
414def return_range( function, get_size_class, value_policies=None ):
415    """create `Py++` defined return_range call policies code generator"""
416    r_type = function.return_type
417    if not declarations.is_pointer( r_type ):
418        raise TypeError( 'Function "%s" return type should be pointer, got "%s"'
419                         % r_type.decl_string )
420
421    value_type = declarations.remove_pointer( r_type )
422    if None is value_policies:
423        if python_traits.is_immutable( value_type ):
424            value_policies = default_call_policies()
425        else:
426            raise RuntimeError( "return_range call policies requieres specification of value_policies" )
427    return return_range_t( get_size_class, value_type, value_policies )
428
429