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