|  | @@ -1,5 +1,6 @@
 | 
	
		
			
				|  |  |  from configparser import ConfigParser
 | 
	
		
			
				|  |  |  from binascii import hexlify, unhexlify
 | 
	
		
			
				|  |  | +from datetime import datetime
 | 
	
		
			
				|  |  |  from itertools import islice
 | 
	
		
			
				|  |  |  import errno
 | 
	
		
			
				|  |  |  import logging
 | 
	
	
		
			
				|  | @@ -84,6 +85,7 @@ class Repository:
 | 
	
		
			
				|  |  |          config.set('repository', 'version', '1')
 | 
	
		
			
				|  |  |          config.set('repository', 'segments_per_dir', str(self.DEFAULT_SEGMENTS_PER_DIR))
 | 
	
		
			
				|  |  |          config.set('repository', 'max_segment_size', str(self.DEFAULT_MAX_SEGMENT_SIZE))
 | 
	
		
			
				|  |  | +        config.set('repository', 'append_only', '0')
 | 
	
		
			
				|  |  |          config.set('repository', 'id', hexlify(os.urandom(32)).decode('ascii'))
 | 
	
		
			
				|  |  |          self.save_config(path, config)
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -105,6 +107,8 @@ class Repository:
 | 
	
		
			
				|  |  |      def destroy(self):
 | 
	
		
			
				|  |  |          """Destroy the repository at `self.path`
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  | +        if self.append_only:
 | 
	
		
			
				|  |  | +            raise ValueError(self.path + " is in append-only mode")
 | 
	
		
			
				|  |  |          self.close()
 | 
	
		
			
				|  |  |          os.remove(os.path.join(self.path, 'config'))  # kill config first
 | 
	
		
			
				|  |  |          shutil.rmtree(self.path)
 | 
	
	
		
			
				|  | @@ -148,6 +152,7 @@ class Repository:
 | 
	
		
			
				|  |  |              raise self.InvalidRepository(path)
 | 
	
		
			
				|  |  |          self.max_segment_size = self.config.getint('repository', 'max_segment_size')
 | 
	
		
			
				|  |  |          self.segments_per_dir = self.config.getint('repository', 'segments_per_dir')
 | 
	
		
			
				|  |  | +        self.append_only = self.config.getboolean('repository', 'append_only', fallback=False)
 | 
	
		
			
				|  |  |          self.id = unhexlify(self.config.get('repository', 'id').strip())
 | 
	
		
			
				|  |  |          self.io = LoggedIO(self.path, self.max_segment_size, self.segments_per_dir)
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -163,7 +168,8 @@ class Repository:
 | 
	
		
			
				|  |  |          """Commit transaction
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  |          self.io.write_commit()
 | 
	
		
			
				|  |  | -        self.compact_segments(save_space=save_space)
 | 
	
		
			
				|  |  | +        if not self.append_only:
 | 
	
		
			
				|  |  | +            self.compact_segments(save_space=save_space)
 | 
	
		
			
				|  |  |          self.write_index()
 | 
	
		
			
				|  |  |          self.rollback()
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -211,6 +217,9 @@ class Repository:
 | 
	
		
			
				|  |  |          self.index.write(os.path.join(self.path, 'index.tmp'))
 | 
	
		
			
				|  |  |          os.rename(os.path.join(self.path, 'index.tmp'),
 | 
	
		
			
				|  |  |                    os.path.join(self.path, 'index.%d' % transaction_id))
 | 
	
		
			
				|  |  | +        if self.append_only:
 | 
	
		
			
				|  |  | +            with open(os.path.join(self.path, 'transactions'), 'a') as log:
 | 
	
		
			
				|  |  | +                print('transaction %d, UTC time %s' % (transaction_id, datetime.utcnow().isoformat()), file=log)
 | 
	
		
			
				|  |  |          # Remove old indices
 | 
	
		
			
				|  |  |          current = '.%d' % transaction_id
 | 
	
		
			
				|  |  |          for name in os.listdir(self.path):
 | 
	
	
		
			
				|  | @@ -323,6 +332,8 @@ class Repository:
 | 
	
		
			
				|  |  |          This method verifies all segment checksums and makes sure
 | 
	
		
			
				|  |  |          the index is consistent with the data stored in the segments.
 | 
	
		
			
				|  |  |          """
 | 
	
		
			
				|  |  | +        if self.append_only and repair:
 | 
	
		
			
				|  |  | +            raise ValueError(self.path + " is in append-only mode")
 | 
	
		
			
				|  |  |          error_found = False
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          def report_error(msg):
 |