1
2
3
4
5
6 import os
7 import sys
8 import time
9 import types
10 import warnings
11
12 from pygccxml import parser
13 from pygccxml import declarations as decls_package
14
15 from pyplusplus import utils
16 from pyplusplus import _logging_
17 from pyplusplus import decl_wrappers
18 from pyplusplus import file_writers
19 from pyplusplus import code_creators
20 from pyplusplus import module_creator as mcreator_package
23 """
24 This class provides users with simple and intuitive interface to Py++
25 and/or pygccxml functionality. If this is your first attempt to use Py++
26 consider to read tutorials. You can find them on U{web site<http://www.language-binding.net>}.
27 """
28
29 - def __init__( self
30 , files
31 , gccxml_path=''
32 , working_directory='.'
33 , include_paths=None
34 , define_symbols=None
35 , undefine_symbols=None
36 , start_with_declarations=None
37 , compilation_mode=None
38 , cache=None
39 , optimize_queries=True
40 , ignore_gccxml_output=False
41 , indexing_suite_version=1
42 , cflags=""
43 , encoding='ascii'
44 , compiler=None):
45 """
46 @param files: list of files, declarations from them you want to export
47 @type files: list of strings or L{file_configuration_t} instances
48
49 @param gccxml_path: path to gccxml binary. If you don't pass this argument,
50 pygccxml parser will try to locate it using you environment PATH variable
51 @type gccxml_path: str
52
53 @param include_paths: additional header files location. You don't have to
54 specify system and standard directories.
55 @type include_paths: list of strings
56
57 @param define_symbols: list of symbols to be defined for preprocessor.
58 @param define_symbols: list of strings
59
60 @param undefine_symbols: list of symbols to be undefined for preprocessor.
61 @param undefine_symbols: list of strings
62
63 @param cflags: Raw string to be added to gccxml command line.
64 """
65 object.__init__( self )
66 self.logger = _logging_.loggers.module_builder
67 self.__encoding = encoding
68 gccxml_config = parser.config_t(
69 gccxml_path=gccxml_path
70 , working_directory=working_directory
71 , include_paths=include_paths
72 , define_symbols=define_symbols
73 , undefine_symbols=undefine_symbols
74 , start_with_declarations=start_with_declarations
75 , ignore_gccxml_output=ignore_gccxml_output
76 , cflags=cflags
77 , compiler=compiler)
78
79
80
81 self.__working_dir = os.path.abspath( working_directory )
82
83 self.__parsed_files = map( decls_package.filtering.normalize_path
84 , parser.project_reader_t.get_os_file_names( files ) )
85 tmp = map( lambda file_: os.path.split( file_ )[0], self.__parsed_files )
86 self.__parsed_dirs = filter( None, tmp )
87
88 self.__global_ns = self.__parse_declarations( files
89 , gccxml_config
90 , compilation_mode
91 , cache
92 , indexing_suite_version)
93 self.__code_creator = None
94 if optimize_queries:
95 self.run_query_optimizer()
96
97 self.__declarations_code_head = []
98 self.__declarations_code_tail = []
99
100 self.__registrations_code_head = []
101 self.__registrations_code_tail = []
102
103 @property
105 """reference to global namespace"""
106 return self.__global_ns
107
108 @property
110 return self.__encoding
111
113 """``already_exposed`` solution is pretty good when you mix hand-written
114 modules with Py++ generated. It doesn't work/scale for "true"
115 multi-module development. This is exactly the reason why ``Py++``_
116 offers "semi automatic" solution.
117
118 For every exposed module, ``Py++``_ generates "exposed_decl.pypp.txt" file.
119 This file contains the list of all parsed declarations and whether they
120 were included or excluded. Later, when you work on another module, you
121 can tell ``Py++``_ that the current module depends on the previously
122 generated one. ``Py++``_ will load "exposed_decl.pypp.txt" file and
123 update the declarations.
124 """
125
126 db = utils.exposed_decls_db_t()
127 db.load( other_module_generated_code_dir )
128 db.update_decls( self.global_ns )
129
131 """
132 It is possible to optimze time that takes to execute queries. In most cases
133 this is done from __init__ method. But there are use-case, when you need
134 to disable optimizer at __init__ and run it later.
135 """
136 self.__global_ns.init_optimizer()
137
138 - def __parse_declarations( self, files, gccxml_config, compilation_mode, cache, indexing_suite_version ):
165
182
184 flatten_decls = decls_package.make_flatten( decls )
185 self.__filter_by_location( flatten_decls )
186 call_policies_resolver = mcreator_package.built_in_resolver_t()
187 calldefs = filter( lambda decl: isinstance( decl, decls_package.calldef_t )
188 , flatten_decls )
189 map( lambda calldef: calldef.set_call_policies( call_policies_resolver( calldef ) )
190 , calldefs )
191 mem_vars = filter( lambda decl: isinstance( decl, decls_package.variable_t )
192 and isinstance( decl.parent, decls_package.class_t )
193 , flatten_decls )
194 map( lambda mem_var: mem_var.set_getter_call_policies( call_policies_resolver( mem_var, 'get' ) )
195 , mem_vars )
196 map( lambda mem_var: mem_var.set_setter_call_policies( call_policies_resolver( mem_var, 'set' ) )
197 , mem_vars )
198
199 @property
201 "List of user code, that will be added to the head of the declarations section."
202 return self.__declarations_code_head
203
204 @property
206 "List of user code, that will be added to the tail of the declarations section."
207 return self.__declarations_code_tail
208
209 @property
211 "List of user code, that will be added to the head of the registrations section."
212 return self.__registrations_code_head
213
214 @property
216 "List of user code, that will be added to the tail of the registrations section."
217 return self.__registrations_code_tail
218
220 """
221 This function will print detailed description of all declarations or
222 some specific one.
223
224 @param decl: optional, if passed, then only it will be printed
225 @type decl: instance of L{decl_wrappers.decl_wrapper_t} class
226 """
227 if None is decl:
228 decl = self.global_ns
229 decl_wrappers.print_declarations( decl, detailed, recursive, writer )
230
231 - def build_code_creator( self
232 , module_name
233 , boost_python_ns_name='bp'
234 , create_casting_constructor=True
235 , call_policies_resolver_=None
236 , types_db=None
237 , target_configuration=None
238 , enable_indexing_suite=True
239 , doc_extractor=None):
240 """
241 Creates L{module_t} code creator.
242
243 @param module_name: module name
244 @type module_name: string
245
246 @param boost_python_ns_name: boost::python namespace alias, by default
247 it is 'bp'
248 @type boost_python_ns_name: string
249
250 @param call_policies_resolver_: callable, that will be invoked on every
251 calldef object. It should return call policies.
252 @type call_policies_resolver_: callable
253 @param doc_extractor: callable, that takes as argument reference to declaration
254 and returns documentation string
255 @type doc_extractor: callable or None
256 """
257 if not create_casting_constructor:
258 msg = os.linesep.join([
259 "create_casting_constructor argument is deprecated."
260 , "If want to disable boost::python::implicitly_convertible code generation, consider to use allow_implicit_conversion constructor property"
261 , ">>> mb = module_builder_t(...)"
262 , ">>> mb.constructors().allow_implicit_conversion = False"])
263 warnings.warn(msg, DeprecationWarning, stacklevel=2)
264
265 self.global_ns.constructors(allow_empty=True).allow_implicit_conversion = False
266
267 creator = mcreator_package.creator_t( self.global_ns
268 , module_name
269 , boost_python_ns_name
270 , call_policies_resolver_
271 , types_db
272 , target_configuration
273 , enable_indexing_suite
274 , doc_extractor)
275 self.__code_creator = creator.create()
276 self.__code_creator.replace_included_headers(self.__parsed_files)
277
278
279
280
281
282 return self.__code_creator
283
284 @property
286 "reference to L{code_creators.module_t} instance"
287 if not self.__code_creator:
288 raise RuntimeError( "self.module is equal to None. Did you forget to call build_code_creator function?" )
289 return self.__code_creator
290
292 """
293 Function, that will return True if build_code_creator function has been
294 called and False otherwise
295 """
296 return not ( None is self.__code_creator )
297
299 if tail:
300 self.__declarations_code_tail.append( code )
301 else:
302 self.__declarations_code_head.append( code )
303
305 if tail:
306 self.__registrations_code_tail.append( code )
307 else:
308 self.__registrations_code_head.append( code )
309
311 """adds code that exposes some constants to Python.
312
313 For example:
314 mb.add_constants( version='"1.2.3"' )
315 or
316 mb.add_constants( **{ version:'"1.2.3"' } )
317 will generate next code:
318 boost::python::scope().attr("version") = "1.2.3";
319 """
320 tmpl = 'boost::python::scope().attr("%(name)s") = %(value)s;'
321 for name, value in keywds.items():
322 if not isinstance( value, types.StringTypes ):
323 value = str( value )
324 self.add_registration_code( tmpl % dict( name=name, value=value) )
325
326
341
342
351
353 all_files = os.listdir( dir_name )
354 all_files = map( lambda fname: os.path.join( dir_name, fname ), all_files )
355 all_files = filter( file_writers.has_pypp_extenstion, all_files )
356
357 unused_files = set( all_files ).difference( set( written_files ) )
358 for fpath in unused_files:
359 try:
360 if on_unused_file_found is os.remove:
361 self.logger.info( 'removing file "%s"' % fpath )
362 on_unused_file_found( fpath )
363 except Exception, error:
364 self.logger.exception( "Exception was catched, while executing 'on_unused_file_found' function." )
365
366 - def split_module( self
367 , dir_name
368 , huge_classes=None
369 , on_unused_file_found=os.remove
370 , use_files_sum_repository=False):
371 """
372 Writes module to multiple files
373
374 @param dir_name: directory name
375 @type dir_name: string
376
377 @param huge_classes: list that contains reference to classes, that should be split
378
379 @param on_unused_file_found: callable object that represents the action that should be taken on
380 file, which is no more in use
381
382 @use_files_sum_repository: Py++ can generate file, which will contain md5 sum of every generated file.
383 Next time you generate code, md5sum will be loaded from the file and compared.
384 This could speed-up code generation process by 10-15%.
385 """
386 self.__merge_user_code()
387
388 files_sum_repository = None
389 if use_files_sum_repository:
390 cache_file = os.path.join( dir_name, self.code_creator.body.name + '.md5.sum' )
391 files_sum_repository = file_writers.cached_repository_t( cache_file )
392
393 written_files = []
394 if None is huge_classes:
395 written_files = file_writers.write_multiple_files(
396 self.code_creator
397 , dir_name
398 , files_sum_repository=files_sum_repository
399 , encoding=self.encoding)
400 else:
401 written_files = file_writers.write_class_multiple_files(
402 self.code_creator
403 , dir_name
404 , huge_classes
405 , files_sum_repository=files_sum_repository
406 , encoding=self.encoding)
407 self.__work_on_unused_files( dir_name, written_files, on_unused_file_found )
408
409 return written_files
410
411 - def balanced_split_module( self
412 , dir_name
413 , number_of_files
414 , on_unused_file_found=os.remove
415 , use_files_sum_repository=False):
416 """
417 Writes module to fixed number of multiple cpp files
418
419 @param number_of_files: the desired number of generated cpp files
420 @type number_of_files: int
421
422 @param dir_name: directory name
423 @type dir_name: string
424
425 @param on_unused_file_found: callable object that represents the action that should be taken on
426 file, which is no more in use
427
428 @use_files_sum_repository: Py++ can generate file, which will contain md5 sum of every generated file.
429 Next time you generate code, md5sum will be loaded from the file and compared.
430 This could speed-up code generation process by 10-15%.
431 """
432 self.__merge_user_code()
433
434 files_sum_repository = None
435 if use_files_sum_repository:
436 cache_file = os.path.join( dir_name, self.code_creator.body.name + '.md5.sum' )
437 files_sum_repository = file_writers.cached_repository_t( cache_file )
438
439 written_files = file_writers.write_balanced_files( self.code_creator
440 , dir_name
441 , number_of_buckets=number_of_files
442 , files_sum_repository=files_sum_repository
443 , encoding=self.encoding)
444
445 self.__work_on_unused_files( dir_name, written_files, on_unused_file_found )
446
447 return written_files
448
449
450
451 - def decl( self, name=None, function=None, header_dir=None, header_file=None, recursive=None ):
458
459 - def decls( self, name=None, function=None, header_dir=None, header_file=None, recursive=None ):
466
467 - def class_( self, name=None, function=None, header_dir=None, header_file=None, recursive=None ):
474
475 - def classes( self, name=None, function=None, header_dir=None, header_file=None, recursive=None ):
482
483 - def variable( self, name=None, function=None, type=None, header_dir=None, header_file=None, recursive=None ):
491 var = variable
492
493 - def variables( self, name=None, function=None, type=None, header_dir=None, header_file=None, recursive=None ):
501 vars = variables
502
503 - def calldef( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
512
513 - def calldefs( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
522
523 - def operator( self, name=None, symbol=None, return_type=None, arg_types=None, decl_type=None, header_dir=None, header_file=None, recursive=None ):
533
534 - def operators( self, name=None, symbol=None, return_type=None, arg_types=None, decl_type=None, header_dir=None, header_file=None, recursive=None ):
544
545 - def member_function( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
554 mem_fun = member_function
555
556 - def member_functions( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
565
566 mem_funs = member_functions
567
568 - def constructor( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
577
578 - def constructors( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
587
588 - def member_operator( self, name=None, function=None, symbol=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
598
599 - def member_operators( self, name=None, function=None, symbol=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
609
610 - def casting_operator( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
619
620 - def casting_operators( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
629
630 - def enumeration( self, name=None, function=None, header_dir=None, header_file=None, recursive=None ):
637 enum = enumeration
638
639 - def enumerations( self, name=None, function=None, header_dir=None, header_file=None, recursive=None ):
646
647 enums = enumerations
648
649 - def namespace( self, name=None, function=None, recursive=None ):
654
655 - def namespaces( self, name=None, function=None, recursive=None ):
660
661 - def free_function( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
670 free_fun = free_function
671
672 - def free_functions( self, name=None, function=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
681 free_funs = free_functions
682
683 - def free_operator( self, name=None, function=None, symbol=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
693
694 - def free_operators( self, name=None, function=None, symbol=None, return_type=None, arg_types=None, header_dir=None, header_file=None, recursive=None ):
704
709 BOOST_PYTHON_MAX_ARITY = property( _get_BOOST_PYTHON_MAX_ARITY, _set_BOOST_PYTHON_MAX_ARITY )
710