~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/basicio.py

  • Committer: Martin Pool
  • Date: 2005-11-04 01:46:31 UTC
  • mto: (1185.33.49 bzr.dev)
  • mto: This revision was merged to the branch mainline in revision 1512.
  • Revision ID: mbp@sourcefrog.net-20051104014631-750e0ad4172c952c
Make biobench directly executable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""basic_io - simple text metaformat
 
18
 
 
19
The stored data consists of a series of *stanzas*, each of which contains
 
20
*fields* identified by an ascii name.  The contents of each field can be
 
21
either an integer (scored in decimal) or a Unicode string.
 
22
"""
 
23
 
 
24
import re
 
25
 
 
26
# XXX: basic_io is kind of a dumb name; it seems to imply an io layer not a
 
27
# format
 
28
#
 
29
# XXX: some redundancy is allowing to write stanzas in isolation as well as
 
30
# through a writer object.  
 
31
 
 
32
class BasicWriter(object):
 
33
    def __init__(self, to_file):
 
34
        self._soft_nl = False
 
35
        self._to_file = to_file
 
36
 
 
37
    def write_stanza(self, stanza):
 
38
        if self._soft_nl:
 
39
            print >>self._to_file
 
40
        stanza.write(self._to_file)
 
41
        self._soft_nl = True
 
42
 
 
43
 
 
44
class BasicReader(object):
 
45
    """Read stanzas from a file as a sequence
 
46
    
 
47
    to_file can be anything that can be enumerated as a sequence of 
 
48
    lines (with newlines.)
 
49
    """
 
50
    def __init__(self, from_file):
 
51
        self._from_file = from_file
 
52
 
 
53
    def __iter__(self):
 
54
        while True:
 
55
            s = read_stanza(self._from_file)
 
56
            if s is None:
 
57
                break
 
58
            else:
 
59
                yield s
 
60
 
 
61
def read_stanzas(from_file):
 
62
    while True:
 
63
        s = read_stanza(from_file)
 
64
        if s is None:
 
65
            break
 
66
        else:
 
67
            yield s
 
68
 
 
69
class Stanza(object):
 
70
    """One stanza for basic_io.
 
71
 
 
72
    Each stanza contains a set of named fields.  
 
73
    
 
74
    Names must be non-empty ascii alphanumeric plus _.  Names can be repeated
 
75
    within a stanza.  Names are case-sensitive.  The ordering of fields is
 
76
    preserved.
 
77
 
 
78
    Each field value must be either an int or a string.
 
79
    """
 
80
 
 
81
    __slots__ = ['items']
 
82
 
 
83
    def __init__(self, **kwargs):
 
84
        """Construct a new Stanza.
 
85
 
 
86
        The keyword arguments, if any, are added in sorted order to the stanza.
 
87
        """
 
88
        if kwargs:
 
89
            self.items = sorted(kwargs.items())
 
90
        else:
 
91
            self.items = []
 
92
 
 
93
    def add(self, tag, value):
 
94
        """Append a name and value to the stanza."""
 
95
##         if not valid_tag(tag):
 
96
##             raise ValueError("invalid tag %r" % tag)
 
97
##         if not isinstance(value, (int, long, str, unicode)):
 
98
##             raise ValueError("invalid value %r" % value)
 
99
        self.items.append((tag, value))
 
100
        
 
101
    def __contains__(self, find_tag):
 
102
        """True if there is any field in this stanza with the given tag."""
 
103
        for tag, value in self.items:
 
104
            if tag == find_tag:
 
105
                return True
 
106
        return False
 
107
 
 
108
    def __len__(self):
 
109
        """Return number of pairs in the stanza."""
 
110
        return len(self.items)
 
111
 
 
112
    def __eq__(self, other):
 
113
        if not isinstance(other, Stanza):
 
114
            return False
 
115
        return self.items == other.items
 
116
 
 
117
    def __ne__(self, other):
 
118
        return not self.__eq__(other)
 
119
 
 
120
    def __repr__(self):
 
121
        return "Stanza(%r)" % self.items
 
122
 
 
123
    def iter_pairs(self):
 
124
        """Return iterator of tag, value pairs."""
 
125
        return iter(self.items)
 
126
 
 
127
    def to_lines(self):
 
128
        """Generate sequence of lines for external version of this file."""
 
129
        for tag, value in self.items:
 
130
            if isinstance(value, (int, long)):
 
131
                # must use %d so bools are written as ints
 
132
                yield '%s %d\n' % (tag, value)
 
133
            else:
 
134
                assert isinstance(value, (str, unicode)), ("invalid value %r" % value)
 
135
                qv = value.replace('\\', r'\\') \
 
136
                          .replace('"',  r'\"') \
 
137
                          .replace('\n', r'\n')
 
138
                yield '%s "%s"\n' % (tag, qv)
 
139
 
 
140
    def to_string(self):
 
141
        """Return stanza as a single string"""
 
142
        return ''.join(self.to_lines())
 
143
 
 
144
    def write(self, to_file):
 
145
        """Write stanza to a file"""
 
146
        to_file.writelines(self.to_lines())
 
147
 
 
148
    def get(self, tag):
 
149
        """Return the value for a field wih given tag.
 
150
 
 
151
        If there is more than one value, only the first is returned.  If the
 
152
        tag is not present, KeyError is raised.
 
153
        """
 
154
        for t, v in self.items:
 
155
            if t == tag:
 
156
                return v
 
157
        else:
 
158
            raise KeyError(tag)
 
159
 
 
160
    __getitem__ = get
 
161
 
 
162
    def get_all(self, tag):
 
163
        r = []
 
164
        for t, v in self.items:
 
165
            if t == tag:
 
166
                r.append(v)
 
167
        return r
 
168
         
 
169
TAG_RE = re.compile(r'^[-a-zA-Z0-9_]+$')
 
170
def valid_tag(tag):
 
171
    return bool(TAG_RE.match(tag))
 
172
 
 
173
 
 
174
def read_stanza(from_lines):
 
175
    """Return new Stanza read from list of lines or a file"""
 
176
    items = []
 
177
    for l in from_lines:
 
178
        if l == None or l == '' or l == '\n':
 
179
            break
 
180
        assert l[-1] == '\n'
 
181
        space = l.index(' ')
 
182
        tag = l[:space]
 
183
        assert valid_tag(tag), \
 
184
                "invalid basic_io tag %r" % tag
 
185
        rest = l[space+1:]
 
186
        if l[space+1] == '"':
 
187
            # keep reading in lines, accumulating into value, until we're done
 
188
            assert l[-2] == '"'
 
189
            value = l[space+2:-2]
 
190
            value = value.replace(r'\n', '\n') \
 
191
                  .replace(r'\"', '\"') \
 
192
                  .replace(r'\\', '\\')
 
193
        else:
 
194
            value = int(l[space+1:])
 
195
        items.append((tag, value))
 
196
    if not items:
 
197
        return None         # didn't see any content
 
198
    s = Stanza()
 
199
    s.items = items
 
200
    return s
 
201
 
 
202
 
 
203
############################################################
 
204
 
 
205
# XXX: Move these to object serialization code. 
 
206
 
 
207
def write_revision(writer, revision):
 
208
    s = Stanza(revision=revision.revision_id,
 
209
               committer=revision.committer, 
 
210
               timezone=long(revision.timezone),
 
211
               timestamp=long(revision.timestamp),
 
212
               inventory_sha1=revision.inventory_sha1)
 
213
    for parent_id in revision.parent_ids:
 
214
        s.add('parent', parent_id)
 
215
    for prop_name, prop_value in revision.properties.items():
 
216
        s.add(prop_name, prop_value)
 
217
    s.write(writer)
 
218
 
 
219
def write_inventory(writer, inventory):
 
220
    s = Stanza(inventory_version=7)
 
221
    writer.write_stanza(s)
 
222
 
 
223
    for path, ie in inventory.iter_entries():
 
224
        s = Stanza()
 
225
        s.add(ie.kind, ie.file_id)
 
226
        for attr in ['name', 'parent_id', 'revision',
 
227
                     'text_sha1', 'text_size', 'executable', 'symlink_target',
 
228
                     ]:
 
229
            attr_val = getattr(ie, attr, None)
 
230
            if attr == 'executable' and attr_val == 0:
 
231
                continue
 
232
            if attr == 'parent_id' and attr_val == 'TREE_ROOT':
 
233
                continue
 
234
            if attr_val is not None:
 
235
                s.add(attr, attr_val)
 
236
        writer.write_stanza(s)
 
237
 
 
238
 
 
239
def read_inventory(inv_file):
 
240
    """Read inventory object from basic_io formatted inventory file"""
 
241
    from bzrlib.inventory import Inventory, InventoryFile
 
242
    s = read_stanza(inv_file)
 
243
    assert s['inventory_version'] == 7
 
244
    inv = Inventory()
 
245
    for s in read_stanzas(inv_file):
 
246
        kind, file_id = s.items[0]
 
247
        parent_id = None
 
248
        if 'parent_id' in s:
 
249
            parent_id = s['parent_id']
 
250
        if kind == 'file':
 
251
            ie = InventoryFile(file_id, s['name'], parent_id)
 
252
            ie.text_sha1 = s['text_sha1']
 
253
            ie.text_size = s['text_size']
 
254
        else:
 
255
            raise NotImplementedError()
 
256
        inv.add(ie)
 
257
    return inv