File: info_file.py

package info (click to toggle)
python-enthoughtbase 3.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 960 kB
  • ctags: 1,034
  • sloc: python: 6,104; makefile: 9; sh: 5
file content (197 lines) | stat: -rw-r--r-- 6,528 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197



import md5
import os
import re
import warnings
from xml.etree import ElementTree as ET


#========================================================================
# Regex helpers for parsing file names and validating checksums.
#========================================================================

_version_in_name = re.compile("(\S*)[-](\d+\.\d+\.*\d*)\S*")

def _get_filename(filename):
    match = _version_in_name.search(filename)
    if match is None:
        raise ValueError, "Could not find name in filename: " + filename
    return match.group(1)

def _get_version(filename):
    match = _version_in_name.search(filename)
    if match is None:
        raise ValueError, "Could not find version in filename: " + filename
    return match.group(2)

def _get_checksum(filename):
    base, ext = os.path.splitext(filename)
    data = open(base).read()
    return md5.new(data).hexdigest()


filename_re = re.compile('filename: \s*(.*)\n')
version_re = re.compile('version: \s*(.*)\n')
checksum_re = re.compile('checksum: \s*(.*)\n')
desc_re = re.compile('\ndescription:\n')

codedict = {'filename':{'re':filename_re,
                    'get':_get_filename},
            'version': {'re':version_re,
                        'get':_get_version},
            'checksum': {'re':checksum_re,
                         'get':_get_checksum}
            }

class InfoFile:
    """Representation of an .info file, which provides metadata of another
    file (its "target").

    Important methods: 
    
    @classmethod
    from_info_file(filename)

      construct an InfoFile object from a filename --- simple parser

      name: %filename% (if not present extracted from .info filename)
      version: %filename% (if not present it is extracted from name of file)
      checksum: md5hash (if not present it is computed from the basefile)
      html: (everything else in the file from the next line to the end)
    
    get_xml()
      return a list of xml elements for this file       
    """

    # The filename of the update_file. This is not the full path - 
    # see **location_root** below.
    filename = ""

    # The version of the target file
    version = None

    # Checksum of the target file
    checksum = None

    # A multi-line HTML document describing the changes between
    # this version and the previous version
    description = ""

    # The reported location of where self.filename can be found.  This gets
    # prepended to self.filename to form the full path.  Typically this will be
    # an HTTP URL, but this can be a URI for a local or LAN directory.
    # This field usually gets set by an external tool, and is not present
    # in the .info format.
    location = "./"

    # A function that takes a string (self.version) and returns something
    # that can be used to compare against the version-parsed version of
    # another VersionInfo object.
    version_parser = None

    #========================================================================
    # Constructors
    #========================================================================

    @classmethod
    def from_info_file(cls, filename):
        """ Construct an InfoFile instance from a .info file on disk.
        """
        str = open(filename).read()
        obj = cls()
        for attr in ['filename', 'version', 'checksum']:
            funcdict = codedict[attr]
            match = funcdict['re'].search(str)
            if match is None:
                value = funcdict['get'](filename)
            else:
                value = match.group(1)
            setattr(obj, attr, value)
            
        match = desc_re.search(str)
        if match is None:
            warnings.warn("Info file " + filename + " lacks a description: field")
        else:
            beg, end = match.span()
            start = str.find('\n', end)
            obj.description = str[start:]
        return obj

    @classmethod
    def from_target_file(cls, filename):
        """ Construct an InfoFile given the filename of the target file.
        """
        obj = cls(filename=filename)

        # Try to glean a version number from the file name
        try:
            version = _get_version(filename)
            obj.version = version
        except ValueError:
            pass
        return obj

    @classmethod
    def from_xml(cls, bytes):
        """ Returns a new InfoFile instance from a multi-line string of
        XML data
        """
        raise NotImplementedError

    def __init__(self, **kwargs):
        # Do a strict Traits-like construction
        for attr in ("filename", "version", "checksum", "description", 
                     "location", "version_parser"):
            if attr in kwargs:
                setattr(self, attr, kwargs[attr])
        return

    #========================================================================
    # Public methods
    #========================================================================

    def to_xml(self):
        root = ET.Element("file")
        for attrname in ("version", "filename", "location", "checksum", "description"):
            node = ET.SubElement(root, attrname)
            node.text = getattr(self, attrname)
        return root

    def to_xml_str(self):
        """ Returns a multi-line string of XML representing the information in
        this object.
        """
        return ET.tostring(self.to_xml())

    def to_info_str(self):
        """ Returns a multi-line string in the .info file format 
        """
        lines = []
        for attr in ["filename", "version", "checksum"]:
            lines.append(attr + ": " + getattr(self, attr))
        return "\n".join(lines) + "\ndescription:\n" + self.description + "\n"

    def __cmp__(self, other):
        """ Allows for comparing two VersionInfo objects so they can
        be presented in version-sorted order.  This is where we parse
        and interpretation of the **version** string attribute.
        """
        # TODO: Do something more intelligent here, if version parsers are missing
        if self.version_parser is not None:
            self_ver = self.version_parser(self.version)
        else:
            self_ver = self.version
        if other.version_parser is not None:
            other_ver = other.version_parser(other.version)
        else:
            other_ver = other.version
        if self_ver < other_ver:
            return -1
        elif self.ver == other_ver:
            return 0
        else:
            return 1