#!/usr/bin/env python ############################################################################# ## # This file is part of Taurus ## # http://taurus-scada.org ## # Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain ## # Taurus is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. ## # Taurus is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. ## # You should have received a copy of the GNU Lesser General Public License # along with Taurus. If not, see . ## ############################################################################# """ propertyfile.py: A Python replacement for java.util.Properties class This is modelled as closely as possible to the Java original. Adapted from http://code.activestate.com/recipes/496795/ Created - Anand B Pillai Modified - Tiago Coutinho """ from builtins import next from builtins import object import sys import re import time __all__ = ["Properties"] __docformat__ = "restructuredtext" class Properties(object): """ A Python replacement for java.util.Properties """ def __init__(self, props=None): # Note: We don't take a default properties object # as argument yet # Dictionary of properties. self._props = {} # Dictionary of properties with 'pristine' keys # This is used for dumping the properties to a file # using the 'store' method self._origprops = {} # Dictionary mapping keys from property # dictionary to pristine dictionary self._keymap = {} self.othercharre = re.compile(r'(?',line # Means we need to split by space. first, last = m2.span() sepidx = first elif m: # print 'Other match=>',line # No matching wspace char found, need # to split by either '=' or ':' first, last = m.span() sepidx = last - 1 # print line[sepidx] # If the last character is a backslash # it has to be preceded by a space in which # case the next line is read as part of the # same property while line[-1] == '\\': # Read next line nextline = next(i) nextline = nextline.strip() lineno += 1 # This line will become part of the value line = line[:-1] + nextline # Now split to key,value according to separation char if sepidx != -1: key, value = line[:sepidx], line[sepidx + 1:] else: key, value = line, '' self.processPair(key, value) def processPair(self, key, value): """ Process a (key, value) pair """ oldkey = key oldvalue = value # Create key intelligently keyparts = self.bspacere.split(key) # print keyparts strippable = False lastpart = keyparts[-1] if lastpart.find('\\ ') != -1: keyparts[-1] = lastpart.replace('\\', '') # If no backspace is found at the end, but empty # space is found, strip it elif lastpart and lastpart[-1] == ' ': strippable = True key = ''.join(keyparts) if strippable: key = key.strip() oldkey = oldkey.strip() oldvalue = self.unescape(oldvalue) value = self.unescape(value) self._props[key] = value.strip() # Check if an entry exists in pristine keys if key in self._keymap: oldkey = self._keymap.get(key) self._origprops[oldkey] = oldvalue.strip() else: self._origprops[oldkey] = oldvalue.strip() # Store entry in keymap self._keymap[key] = oldkey def escape(self, value): # Java escapes the '=' and ':' in the value # string with backslashes in the store method. # So let us do the same. newvalue = value.replace(':', '\:') newvalue = newvalue.replace('=', '\=') return newvalue def unescape(self, value): # Reverse of escape newvalue = value.replace('\:', ':') newvalue = newvalue.replace('\=', '=') return newvalue def load(self, stream): """ Load properties from an open file stream """ # For the time being only accept file input streams if type(stream) is not file: raise TypeError('Argument should be a file object!') # Check for the opened mode if stream.mode != 'r': raise ValueError('Stream should be opened in read-only mode!') try: lines = stream.readlines() self.__parse(lines) except IOError as e: raise def getProperty(self, key): """ Return a property for the given key """ return self._props.get(key, '') def setProperty(self, key, value): """ Set the property for the given key """ if type(key) is str and type(value) is str: self.processPair(key, value) else: raise TypeError('both key and value should be strings!') def propertyNames(self): """ Return an iterator over all the keys of the property dictionary, i.e the names of the properties """ return list(self._props.keys()) def list(self, out=sys.stdout): """ Prints a listing of the properties to the stream 'out' which defaults to the standard output """ out.write('-- listing properties --\n') for key, value in list(self._props.items()): out.write(''.join((key, '=', value, '\n'))) def store(self, out, header=""): """ Write the properties list to the stream 'out' along with the optional 'header' """ if out.mode[0] != 'w': raise ValueError('Steam should be opened in write mode!') try: out.write(''.join(('#', header, '\n'))) # Write timestamp tstamp = time.strftime('%a %b %d %H:%M:%S %Z %Y', time.localtime()) out.write(''.join(('#', tstamp, '\n'))) # Write properties from the pristine dictionary for prop, val in list(self._origprops.items()): out.write(''.join((prop, '=', self.escape(val), '\n'))) out.close() except IOError as e: raise def getPropertyDict(self): return self._props def __getitem__(self, name): """ To support direct dictionary like access """ return self.getProperty(name) def __setitem__(self, name, value): """ To support direct dictionary like access """ self.setProperty(name, value) def __getattr__(self, name): """ For attributes not found in self, redirect to the properties dictionary """ try: return self.__dict__[name] except KeyError: if hasattr(self._props, name): return getattr(self._props, name) if __name__ == "__main__": p = Properties() p.load(open('test2.properties')) p.list() p['hello'] = "no" # print p # print p.items() # print p['name3'] # p['name3'] = 'changed = value' # print p['name3'] # p['new key'] = 'new value' p.store(open('test2.properties', 'w'))