A python replacement for java.util.Properties

A python replacement for java.util.Properties

On http://code.activestate.com/recipes/496795/, it provides a great python replacement for java.util.Properties.

 

Based on that, I made some little improvement, properties file can be stored back, and also it can use {} to refer previous properties.

A key-value pair is separated by '=' or ':', and backslash is used as an escape key.

Some examples of valid key-value pairs:

text                 key              value

a=b=c                a                b=c

a\=b=c               a=b              c

a\\=b=c              a\                b=c

a\\\=b=c             a\=b               c

a\\\\=b=c             a\\                b=c

a=b=c\

z                    a                b=cz

#! /usr/bin/env python

"""

A Python replacement for java.util.Properties class

This is modelled as closely as possible to the Java original.

 

Created - Anand B Pillai <abpillai@gmail.com>

Modified - Yuan Yun <yuanyun.ken@gmail.com>  

 

Reference:    http://code.activestate.com/recipes/496795/

"""

import sys,os

import re

import time                             

class Properties(object):

    """ A Python replacement for java.util.Properties """

   

    def __init__(self, fileName = None):

        self.fileName = fileName

        # Dictionary of properties.

        # format: {

        #        key : {

        #                'LINENOS': []

        #                'LINE_CONTENT': value]       

        #               }

        #        }

        # when read from properties file to _props, key and value are unescaped,

        # and when store to file, contents are escaped.

        # for example in propertis file: a\\\=b\=c,

        # in _props, it is p['a\='] = 'b=c'

        self._props = {}

        #this is used to capture all backslashs in the end

        self.lastBackslashRE = re.compile(r'(?:.*?)(\\+)$')

        #this is used to split key and value

        self.splitKeyValueRE = re.compile(r'^((?:(?:\\.)*?|.*?)+)[=:](.*)$')

        self.curliesRE = re.compile("{.+?}")

        self.CONSTANT_LINENOS = 'LINENOS'

        self.CONSTANT_LINE_CONTENT = 'LINE_CONTENT'

        #a list, containing the content of the properties file

        self.lines = []

        if fileName is not None:

            self.load(fileName)

              

    def load(self, fileName):

        """ Load properties from a file """

        try:

            self.fileName = fileName

            self.lines = open(fileName).readlines( )

            self.__parse(self.lines)

        except IOError, e:

            raise           

 

    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 separated by '=' or ':'.

         and backslash is used as a escape key, that means '\=' or '\:' is not separator,but is normal '=',':',

         '\\' means '\'.

         also if line's last character is '\', the key-value pair would include next line's content

         also it can use {} to refer previous properties,

         prop1=hello

         prop2={prop1} world!

         p['prop2']'s value would be 'hello world'

       

         Some examples of valid key-value pairs:       

         text                 key              value

         a=b=c                a                b=c

         a\=b=c               a=b              c

         a\\=b=c              a\               b=c

         a\\\=b=c             a\=b             c

         a\\\\=b=c            a\\              b=c

         a=b=c\

         z                    a                b=cz        (this support is not well implemented yet)

 

        When use ':' as a separator, all is same.

       

        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=-1

        i = iter(lines)

        # lines have '\n' at end

        for line in i:

            lineno += 1

            key,value = None, None

            valueSpanRows = [lineno]

            #remove line break

            line = line.rstrip('\n')

            # Skip null lines

            if not line.strip(): continue

            # Skip lines which are comments

            if line.strip()[0] == '#': continue

            #if the last char is a real single \, continue to read in the next line           

            while True :

                match = self.lastBackslashRE.match(line)

                if match:

                    backslashs = match.group(1)

                    if len(backslashs) %2 == 1:

                        nextline = i.next().rstrip('\n')

                        lineno += 1

                        #exclude the last backslash of pervious line, This line will become part of the value

                        line = line[:-1] + nextline

                        valueSpanRows.append(lineno)

                    else:

                        break

                else:

                    break

            key,value = self.getKeyAndValue(line)

            if key is not None:

                self._props[key] = {self.CONSTANT_LINENOS:valueSpanRows,self.CONSTANT_LINE_CONTENT:value}

    def getKeyAndValue(self,line):     

        key,value = None,None

        match = self.splitKeyValueRE.match(line)

        if match:

            key, value = match.group(1), match.group(2)

            key = self.unescape(key)

            value = self.unescape(value)

           

            found = self.curliesRE.findall(value)   

            for f in found:

                srcKey = self.unescape(f[1:-1])  

                if self._props.has_key(srcKey):

                    value = value.replace(f, self.getProperty(srcKey), 1)

        return key,value

   

    def unescape(self,value):

        #convert \\, \:, \= to \, :, = accordingly

        #notice the order

        newvalue = value.replace('\\\\','\\')

        newvalue = newvalue.replace('\:',':')

        newvalue = newvalue.replace('\=','=')       

        return newvalue

 

    def escape(self,value):

        #convert \, :, = to \\, \:, \= accordingly

        # Reverse of escape, notice the order

        newvalue = value.replace('\\','\\\\')

        newvalue = newvalue.replace(':','\:')

        newvalue = newvalue.replace('=','\=')       

        return newvalue

 

    def getProperty(self, key):

        """ Return a property for the given key """

        if self._props.has_key(key):

            return self._props.get(key)[self.CONSTANT_LINE_CONTENT]

        else:

            return None

 

    def setProperty(self, key, value):

        """ Set the property for the given key """

        if type(key) is str and type(value) is str:

            # Check if an entry exists in pristine keys

            if self._props.has_key(key):        

                lineno = self._props[key][self.CONSTANT_LINENOS][0]

                self._props[key] = {self.CONSTANT_LINENOS:[lineno],self.CONSTANT_LINE_CONTENT:value}

                #overwrite the original value in self.lines

                self.lines[lineno] = self.escape(key) + '=' + self.escape(value)                                                             

            else:                        

                linenos = [len(self.lines) + 1]

                self.lines.append(self.escape(key) + '=' + self.escape(value))

                self._props[key] = {self.CONSTANT_LINENOS:linenos,self.CONSTANT_LINE_CONTENT: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 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 self._props.items():

            out.write(''.join((key,'=',self.getProperty(key),'\n')))

 

    def store(self):

        if self.fileName is None:

            raise 'FileName is not specified yet!!'

        self.storeToFile(self.fileName)

       

    def storeToFile(self, outFileName):

        """ Write the properties list to the stream 'out' along

        with the optional 'header' """

        try:

            #each line doesn't include line break

            file = open(outFileName, 'w')

            i = 0

            lenght = len(self.lines)  

            while i < lenght:               

                line = self.lines[i]

                file.write(line)

                if i != lenght-1 and not line.endswith('\n'):

                     file.write('\n')

                i +=1

        except IOError, e:

            raise

       

    def getPropertyDict(self):

        return self._props

 

    def __getitem__(self, key):

        """ To support direct dictionary like access """

        return self.getProperty(key)

 

    #if name exists,this would overwrite its value, otherwise this would append 'name=vaule' to the end

    def __setitem__(self, key, value):

        """ To support direct dictionary like access """

 

        self.setProperty(key, 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)

   

    def __str__(self):

        s='{'

        for key,value in self._props.items():

            s = ''.join((s,key,'=',value,', '))

        s=''.join((s[:-2],'}'))

        return s

 

if __name__=="__main__":

  

    p = Properties("test.properties")   

    p['a=b'] = 'c'

    p['a\\'] = 'b=c'

    p['a\=b'] = 'c'

    p['a\\\\'] = 'b=c'

   

    p['name'] = 'King'

    p['age'] = '20'

    p['name3'] = 'changed = value'

    p['new key'] = 'new value'

   

    p['prop1'] = 'hello'

    p['prop2'] = '{prop1} world!'

    p.store()

    p.storeToFile('test2.properties')

       

    p2 = Properties()

    p2.load('test2.properties')

   

    print "p2['a=b']:" + p2['a=b']

    print "p2['a\\']:" + p2['a\\']

    print "p2['a\=b']:" + p2['a\=b']

    print "p2['a\\\\']:" + p2['a\\\\']

    print "p2['prop2']:" + p2['prop2']

    p2['123'] = '456'

    p2.list()

p2.store()

Resources:

http://code.activestate.com/recipes/496795/

http://groups.google.com/group/comp.lang.perl.misc/browse_thread/thread/8f6ea922a0ca81b2/1106ed0b57e1b24f?lnk=gst&q=yuanyun.ken&pli=1



Post a Comment

Labels

Java (159) Lucene-Solr (110) All (60) Interview (59) J2SE (53) Algorithm (37) Eclipse (35) Soft Skills (35) Code Example (31) Linux (26) JavaScript (23) Spring (22) Windows (22) Web Development (20) Tools (19) Nutch2 (18) Bugs (17) Debug (15) Defects (14) Text Mining (14) J2EE (13) Network (13) PowerShell (11) Chrome (9) Continuous Integration (9) How to (9) Learning code (9) Performance (9) UIMA (9) html (9) Design (8) Dynamic Languages (8) Http Client (8) Maven (8) Security (8) Trouble Shooting (8) bat (8) blogger (8) Big Data (7) Google (7) Guava (7) JSON (7) Problem Solving (7) ANT (6) Coding Skills (6) Database (6) Scala (6) Shell (6) css (6) Algorithm Series (5) Cache (5) IDE (5) Lesson Learned (5) Miscs (5) Programmer Skills (5) System Design (5) Tips (5) adsense (5) xml (5) AIX (4) Code Quality (4) GAE (4) Git (4) Good Programming Practices (4) Jackson (4) Memory Usage (4) OpenNLP (4) Project Managment (4) Python (4) Spark (4) Testing (4) ads (4) regular-expression (4) Android (3) Apache Spark (3) Become a Better You (3) Concurrency (3) Eclipse RCP (3) English (3) Firefox (3) Happy Hacking (3) IBM (3) J2SE Knowledge Series (3) JAX-RS (3) Jetty (3) Restful Web Service (3) Script (3) regex (3) seo (3) .Net (2) Android Studio (2) Apache (2) Apache Procrun (2) Architecture (2) Batch (2) Build (2) Building Scalable Web Sites (2) C# (2) C/C++ (2) CSV (2) Career (2) Cassandra (2) Distributed (2) Fiddler (2) Google Drive (2) Gson (2) Html Parser (2) Http (2) Image Tools (2) JQuery (2) Jersey (2) LDAP (2) Life (2) Logging (2) Software Issues (2) Storage (2) Text Search (2) xml parser (2) AOP (1) Application Design (1) AspectJ (1) Bit Operation (1) Chrome DevTools (1) Cloud (1) Codility (1) Data Mining (1) Data Structure (1) ExceptionUtils (1) Exif (1) Feature Request (1) FindBugs (1) Greasemonkey (1) HTML5 (1) Httpd (1) I18N (1) IBM Java Thread Dump Analyzer (1) JDK Source Code (1) JDK8 (1) JMX (1) Lazy Developer (1) Mac (1) Machine Learning (1) Mobile (1) My Plan for 2010 (1) Netbeans (1) Notes (1) Operating System (1) Perl (1) Problems (1) Product Architecture (1) Programming Life (1) Quality (1) Redhat (1) Redis (1) Review (1) RxJava (1) Solutions logs (1) Team Management (1) Thread Dump Analyzer (1) Troubleshooting (1) Visualization (1) boilerpipe (1) htm (1) ongoing (1) procrun (1) rss (1)

Popular Posts