Package pyplusplus :: Package file_writers :: Module writer

# Source Code for Module pyplusplus.file_writers.writer

      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  """defines interface for all classes that writes L{code_creators.module_t} to file(s)"""
      7
      8  import os
      9  import time
     10  import codecs
     11  import md5sum_repository
     12  from pyplusplus import utils
     13  from pyplusplus import _logging_
     14  from pyplusplus import code_creators
     15  from pyplusplus import code_repository
     16
     17 -class writer_t(object):
     18      """Base class for all module/code writers.
     19
     20      All writers should have similar usage::
     21
     22        w = writer_class(module, file, ...)
     23        w.write()
     24      """
     25      logger = _logging_.loggers.file_writer
     26
     27 -    def __init__(self, extmodule, files_sum_repository=None, encoding='ascii'):
     28          object.__init__(self)
     29          self.__extmodule = extmodule
     30          self.__files_sum_repository = files_sum_repository
     31          self.__encoding=encoding
     32          if None is files_sum_repository:
     33              self.__files_sum_repository = md5sum_repository.dummy_repository_t()
     34          self.__exposed_decls_db = utils.exposed_decls_db_t()
     35          self.__exposed_decls_db.register_decls( extmodule.global_ns
     36                                                  , extmodule.specially_exposed_decls )
     37
     38      @property
     39 -    def encoding( self ):
     40          """encoding name used to write generated code to files"""
     41          return self.__encoding
     42
     43      @property
     44 -    def extmodule(self):
     45          """The root of the code creator tree ( code_creators.module_t )"""
     46          return self.__extmodule
     47
     48      @property
     49 -    def files_sum_repository( self ):
     50          return self.__files_sum_repository
     51
     52 -    def write(self):
     53          """ Main write method.  Should be overridden by derived classes. """
     54          raise NotImplementedError()
     55
     56      @staticmethod
     57 -    def create_backup(fpath):
     58          """creates backup of the file, by renaming it to C{fpath + ~}"""
     59          if not os.path.exists( fpath ):
     60              return
     61          backup_fpath = fpath + '~'
     62          if os.path.exists( backup_fpath ):
     63              os.remove( backup_fpath )
     64          os.rename( fpath, backup_fpath )
     65
     66 -    def write_code_repository(self, dir):
     67          """creates files defined in L{code_repository} package"""
     68          system_headers = self.extmodule.get_system_headers( recursive=True )
     69          for cr in code_repository.all:
     70              if cr.file_name in system_headers:
     71                  #check whether file from code repository is used
     72                  self.write_file( os.path.join( dir, cr.file_name ), cr.code )
     73          #named_tuple.py is a special case :-(
     74          self.write_file( os.path.join( dir, code_repository.named_tuple.file_name )
     75                           , code_repository.named_tuple.code )
     76      @staticmethod
     77 -    def write_file( fpath, content, files_sum_repository=None, encoding='ascii' ):
     78          """Write a source file.
     79
     80          This method writes the string content into the specified file.
     81          An additional fixed header is written at the top of the file before
     82          content.
     83
     84          @param fpath: File name
     85          @type fpath: str
     86          @param content: The content of the file
     87          @type content: str
     88          """
     89          fname = os.path.split( fpath )[1]
     90          writer_t.logger.debug( 'write code to file "%s" - started' % fpath )
     91          start_time = time.clock()
     92          fcontent_new = []
     93          if os.path.splitext( fpath )[1] == '.py':
     94              fcontent_new.append( '# This file has been generated by Py++.' )
     95          else:
     96              fcontent_new.append( '// This file has been generated by Py++.' )
     97          fcontent_new.append( os.linesep * 2 )
     98          fcontent_new.append( content )
     99          fcontent_new.append( os.linesep ) #keep gcc happy
    100          fcontent_new = ''.join( fcontent_new )
    101          if not isinstance( fcontent_new, unicode ):
    102              fcontent_new = unicode( fcontent_new, encoding )
    103
    104          new_hash_value = None
    105          curr_hash_value = None
    106          if files_sum_repository:
    107              new_hash_value  = files_sum_repository.get_text_value( fcontent_new )
    108              curr_hash_value = files_sum_repository.get_file_value( fname )
    109              if new_hash_value == curr_hash_value:
    110                  writer_t.logger.debug( 'file was not changed( hash ) - done( %f seconds )'
    111                                         % ( time.clock() - start_time ) )
    112                  return
    113
    114          if None is curr_hash_value and os.path.exists( fpath ):
    115              #It could be a first time the user uses files_sum_repository, don't force him
    116              #to recompile the code
    117              #small optimization to cut down compilation time
    118              f = codecs.open( fpath, 'rb', encoding )
    119              fcontent = f.read()
    120              f.close()
    121              if fcontent == fcontent_new:
    122                  writer_t.logger.debug( 'file was not changed( content ) - done( %f seconds )'
    123                                         % ( time.clock() - start_time ) )
    124                  return
    125
    126          writer_t.logger.debug( 'file changed or it does not exist' )
    127
    128          writer_t.create_backup( fpath )
    129          f = codecs.open( fpath, 'w+b', encoding )
    130          f.write( fcontent_new )
    131          f.close()
    132          if new_hash_value:
    133              files_sum_repository.update_value( fname, new_hash_value )
    134          writer_t.logger.info( 'file "%s" - updated( %f seconds )' % ( fname, time.clock() - start_time ) )
    135
    136 -    def get_user_headers( self, creators ):
    137          headers = []
    138          creators = filter( lambda creator: isinstance( creator, code_creators.declaration_based_t )
    139                             , creators )
    140          map( lambda creator: headers.extend( creator.get_user_headers() )
    141               , creators )
    142          return code_creators.code_creator_t.unique_headers( headers )
    143
    144 -    def save_exposed_decls_db( self, file_path ):
    145          self.__exposed_decls_db.save( file_path )
    146