Source code for taurus.core.util.propertyfile

#!/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 <http://www.gnu.org/licenses/>.
##
#############################################################################

"""
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 <abpillai@gmail.com>
Modified - Tiago Coutinho
"""

from builtins import next
from builtins import object

import sys
import re
import time

__all__ = ["Properties"]

__docformat__ = "restructuredtext"

[docs]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'(?<!\\)(\s*\=)|(?<!\\)(\s*\:)') self.othercharre2 = re.compile(r'(\s*\=)|(\s*\:)') self.bspacere = re.compile(r'\\(?!\s$)') def __str__(self): s = '{' for key, value in self._props.items(): s = ''.join((s, key, '=', value, ', ')) s = ''.join((s[:-2], '}')) return s def __parse(self, lines): """ Parse a list of lines and create an internal property dictionary """ # Every line in the file must consist of either a comment # or a key-value pair. A key-value pair is a line consisting # of a key which is a combination of non-white space characters # The separator character between key-value pairs is a '=', # ':' or a whitespace character not including the newline. # If the '=' or ':' characters are found, in the line, even # keys containing whitespace chars are allowed. # A line with only a key according to the rules above is also # fine. In such case, the value is considered as the empty string. # In order to include characters '=' or ':' in a key or value, # they have to be properly escaped using the backslash character. # Some examples of valid key-value pairs: # # key value # key=value # key:value # key value1,value2,value3 # key value1,value2,value3 \ # value4, value5 # key # This key= this value # key = value1 value2 value3 # Any line that starts with a '#' is considerered a comment # and skipped. Also any trailing or preceding whitespaces # are removed from the key/value. # This is a line parser. It parses the # contents like by line. lineno = 0 i = iter(lines) for line in i: lineno += 1 line = line.strip() # Skip null lines if not line: continue # Skip lines which are comments if line[0] == '#': continue # Some flags escaped = False # Position of first separation char sepidx = -1 # A flag for performing wspace re check flag = 0 # Check for valid space separation # First obtain the max index to which we # can search. m = self.othercharre.search(line) if m: first, last = m.span() start, end = 0, first flag = 1 wspacere = re.compile(r'(?<![\\\=\:])(\s)') else: if self.othercharre2.search(line): # Check if either '=' or ':' is present # in the line. If they are then it means # they are preceded by a backslash. # This means, we need to modify the # wspacere a bit, not to look for # : or = characters. wspacere = re.compile(r'(?<![\\])(\s)') start, end = 0, len(line) m2 = wspacere.search(line, start, end) if m2: # print 'Space match=>',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)
[docs] 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
[docs] 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(':', r'\:') newvalue = newvalue.replace('=', r'\=') return newvalue
[docs] def unescape(self, value): # Reverse of escape newvalue = value.replace(r'\:', ':') newvalue = newvalue.replace(r'\=', '=') return newvalue
[docs] def load(self, stream): """ Load properties from an open file stream """ # 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
[docs] def getProperty(self, key): """ Return a property for the given key """ return self._props.get(key, '')
[docs] 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!')
[docs] 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())
[docs] 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')))
[docs] 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
[docs] 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'))