0 0 1
default
Jean-Francois Pieronne - 1 month ago 2019-12-06 11:23:24
jf.pieronne@laposte.net
Add rms_records.py
1 file changed with 330 insertions and 0 deletions:
↑ Collapse Diff ↑
 
new file 100755
1
 
# -*- coding: iso-8859-15 -*-
2
 

	
3
 
#-------------------------------------------------------------------------------
4
 
# Name:        rms_records
5
 
# Purpose:
6
 
#
7
 
# Author:      jolin
8
 
#
9
 
# Created:     14/05/2016
10
 
# Copyright:   (c) SysGroup
11
 
#-------------------------------------------------------------------------------
12
 
from __future__ import print_function
13
 

	
14
 
__all__ = ['Field', 'Int', 'Float', 'Str', 'Date', 'DateTime', 'TimeDelta', 'Padding', 'Record',
15
 
           'InvalidField', 'WrongNumberValues', 'NoFile']
16
 

	
17
 

	
18
 
import itertools
19
 
import datetime
20
 
import construct #, vmsconstruct
21
 

	
22
 
_field_seq = itertools.count(0)  # to order the fields definition in the Record structures
23
 

	
24
 

	
25
 
class InvalidField(Exception):
26
 
    pass
27
 

	
28
 

	
29
 
class WrongNumberValues(Exception):
30
 
    pass
31
 

	
32
 

	
33
 
class NoFile(Exception):
34
 
    pass
35
 

	
36
 

	
37
 
class Field(object):
38
 
    def __init__(self, field_type, size=None, padchar=None, trim=False, key=None, segment=0):
39
 
        self.field_type = field_type
40
 
        self.size = size
41
 
        self.padchar = padchar if padchar is not None else ' '
42
 
        if key is not None:
43
 
            if not isinstance(key, int) or key < 0:
44
 
                raise ValueError('Key must be an integer >= 0, not %s' % key)
45
 
        self.key = key
46
 
        self.segment = segment
47
 
        self.trim = trim
48
 
        # self.field = Field(field_type)
49
 
        self.name = None
50
 
        self._seq = _field_seq.next()
51
 
        # print('Field', self, self._seq)
52
 
        self._value = self._default_value
53
 

	
54
 
    @property
55
 
    def _default_value(self):
56
 
        if self.field_type in (float, int):
57
 
            return 0
58
 
        return ''.ljust(self.size, self.padchar)
59
 

	
60
 
    def __get__(self, instance, owner):
61
 
        # print('Field/get', instance, owner)
62
 
        if instance is None:
63
 
            return self
64
 
        return self._value
65
 

	
66
 
    def __set__(self, instance, value):
67
 
        # print('Field/set', instance, value)
68
 
        if self.field_type is str:
69
 
            value = value.ljust(self.size, self.padchar)[:self.size]
70
 
            value = value.replace('\x00', self.padchar)
71
 
            if self.trim:
72
 
                value = value.rstrip()
73
 

	
74
 
        self._value = value
75
 

	
76
 
    def __repr__(self):
77
 
        return "<Field %s(%s, l:%s, k:%s>" % (self.name, self.field_type, self.size, self.key)
78
 

	
79
 

	
80
 
class Int(Field):
81
 
    def __init__(self, size=4, key=None):
82
 
        super(Int, self).__init__(int, size, key=key)
83
 

	
84
 

	
85
 
class Float(Field):
86
 
    def __init__(self, key=None):
87
 
        super(Float, self).__init__(float, key=key)
88
 

	
89
 

	
90
 
class Str(Field):
91
 
    def __init__(self, size, padchar=' ', trim=False, key=None):
92
 
        super(Str, self).__init__(str, size, padchar=padchar, trim=trim, key=key)
93
 

	
94
 

	
95
 
class Date(Field):
96
 
    def __init__(self, key=None):
97
 
        super(Date, self).__init__(datetime.date, size=8, key=key)
98
 

	
99
 

	
100
 
class DateTime(Field):
101
 
    def __init__(self, key=None):
102
 
        super(DateTime, self).__init__(datetime.datetime, size=8, key=key)
103
 

	
104
 

	
105
 
class TimeDelta(Field):
106
 
    def __init__(self, key=None):
107
 
        super(TimeDelta, self).__init__(datetime.timedelta, size=8, key=key)
108
 

	
109
 

	
110
 
class Padding(Field):
111
 
    def __init__(self, size):
112
 
        # print('------------------ padding', size)
113
 
        super(Padding, self).__init__(None, size=size)
114
 

	
115
 

	
116
 
class Key(object):
117
 
    def __init__(self, key_num, fields):
118
 
        self.key_num = key_num
119
 
        self.fields = fields
120
 

	
121
 
    @property
122
 
    def value(self):
123
 
        return [f._value for f in self.fields]
124
 

	
125
 
    # def __get__(self, instance, owner):
126
 
    #     if instance is None:
127
 
    #         return self
128
 
    #     # return list of values of fields
129
 
    #     return self.value
130
 

	
131
 

	
132
 
def build_fields_struct(name, fields):
133
 
    fields_struct = []
134
 
    for field in fields:
135
 
        if field.field_type is int:
136
 
            if field.size == 1:
137
 
                struct = construct.SLInt8(field.name)
138
 
            elif field.size == 2:
139
 
                struct = construct.SLInt16(field.name)
140
 
            elif field.size == 8:
141
 
                struct = construct.SLInt64(field.name)
142
 
            else:
143
 
                struct = construct.SLInt32(field.name)
144
 
        # elif field.field_type is float:
145
 
        #     struct = vmsconstruct.VaxFloat(field.name)
146
 
        # elif field.field_type is datetime.datetime:
147
 
        #     struct = vmsconstruct.VMSDateTime(field.name)
148
 
        # elif field.field_type is datetime.date:
149
 
        #     struct = vmsconstruct.VMSDate(field.name)
150
 
        # elif field.field_type is datetime.timedelta:
151
 
        #     struct = vmsconstruct.VMSDeltaTime(field.name)
152
 
        elif field.field_type is None:
153
 
            struct = construct.Padding(field.size)
154
 
        elif isinstance(field.field_type, Record):
155
 
            struct = field.field_type._struct
156
 
        else:
157
 
            struct = construct.String(field.name, field.size, padchar=field.padchar)
158
 
        fields_struct.append(struct)
159
 
    # print('build_fields_struct:', fields_struct)
160
 
    return construct.Struct(name, *fields_struct)
161
 

	
162
 

	
163
 
class RecordMeta(type):
164
 
    # def __init__(self, name, bases, dct):
165
 
    #     pass
166
 

	
167
 
    def __new__(meta, name, bases, dct):
168
 
        # print('name:', name)
169
 
        # print('bases:', bases)
170
 
        # print('dct:', dct)
171
 
        fields = []
172
 
        fields_dict = dict()
173
 
        for field_name, field in dct.items():
174
 
            # print('dct item', field_name, field)
175
 
            if isinstance(field, Field):
176
 
                field.name = field_name
177
 
                fields.append(field)
178
 
                fields_dict[field_name] = field
179
 
        fields.sort(key=lambda f: f._seq)
180
 
        # print('fields:', [f.name for f in fields])
181
 
        # get keys
182
 
        keys = dict()
183
 
        for field in fields:
184
 
            # print(field.name, field.key)
185
 
            if field.key is not None:
186
 
                key = '%d' % field.key
187
 
                if key not in keys:
188
 
                    keys[key] = []
189
 
                keys[key].append(field)
190
 
                # print('kk', keys[key])
191
 

	
192
 
        dct['_fields'] = fields
193
 
        dct['_fields_dict'] = fields_dict
194
 
        dct['_keys'] = keys
195
 
        for key, key_fields in keys.items():
196
 
            key_num = int(key)
197
 
            dct['_key%s' % key] = Key(key_num, key_fields)
198
 
            dct['_struct_key%s' % key] = build_fields_struct('key%s' % key, key_fields)
199
 
            # sort key segments
200
 
            key_fields.sort(key=lambda k: k.segment)
201
 
        # build construct structures for the record
202
 
        dct['_struct'] = build_fields_struct(name, fields)
203
 
        # # set the struct in each field to be able to get the value
204
 
        # for field in fields:
205
 
        #     field._global_struct = dct['_struct']
206
 
        cls = type.__new__(meta, name, bases, dct)
207
 
        return cls
208
 

	
209
 
    def __init__(self, *args, **kw):
210
 
        # print('__init__/metaclass', self, args, kw)
211
 
        type.__init__(self, *args, **kw)
212
 

	
213
 

	
214
 
def _value_str(v):
215
 
    if isinstance(v, (int, float)):
216
 
        return v
217
 
    return '"%s"' % v
218
 

	
219
 

	
220
 
class Record(object):
221
 
    __metaclass__ = RecordMeta
222
 
    _fields = []
223
 
    _fields_dict = dict()
224
 
    _struct = None
225
 

	
226
 
    def __init__(self, **kw):
227
 
        # print('init', kw, self)
228
 
        for k, v in kw.items():
229
 
            # print(k, v)
230
 
            if k not in self._fields_dict:
231
 
                raise InvalidField('%s has no field named %s' % (self.__class__.__name__, k))
232
 
            field = self._fields_dict[k]
233
 
            field._value = v
234
 

	
235
 
    @classmethod
236
 
    def from_file_rec(cls, rec, file=None):
237
 
        """
238
 
        Create an Record instance and fill it with the values from rec
239
 
        :param rec: the record from the RMS file
240
 
        :return: Record instance
241
 
        """
242
 
        parsed_rec = cls._struct.parse(rec)
243
 
        values = dict((field.name, getattr(parsed_rec, field.name)) for field in cls._fields)
244
 
        rec = cls(**values)
245
 
        rec._file = file
246
 
        return rec
247
 

	
248
 
    @property
249
 
    def rms_rec(self):
250
 
        """
251
 
        :return: the RMS rec corresponding to the current values in this instance
252
 
        """
253
 
        # print('to_file_rec/struct:', self._struct)
254
 
        c = construct.Container()
255
 
        for field in self._fields:
256
 
            v = field._value
257
 
            if field.field_type is str:
258
 
                v = v.ljust(field.size, field.padchar)[:field.size]
259
 
            c[field.name] =  v
260
 
        return self._struct.build(c)
261
 

	
262
 
    def update(self):
263
 
        """
264
 
        update data in file
265
 
        :return:
266
 
        """
267
 
        if not self._file:
268
 
            raise NoFile('No file for this record')
269
 
        return self._file.update_current(self)
270
 

	
271
 
    def delete(self):
272
 
        if not self._file:
273
 
            raise NoFile('No file for this record')
274
 
        return self._file.delete_rec(self)
275
 

	
276
 
    @classmethod
277
 
    def key_rec(cls, key_number, *values):
278
 
        """
279
 
        the RMS representation of the key values
280
 
        :param key_number:
281
 
        :param values:
282
 
        :return:
283
 
        """
284
 
        key = getattr(cls, '_key%d' % key_number)
285
 
        struct = getattr(cls, '_struct_key%d' % key_number)
286
 
        if len(values) != len(key.fields):
287
 
            raise WrongNumberValues('You did not provide the exact number of values for key %d' % key_number)
288
 
        c = construct.Container()
289
 
        for i, field in enumerate(key.fields):
290
 
            v = values[i]
291
 
            if field.field_type is str:
292
 
                v = v.ljust(field.size, field.padchar)[:field.size]
293
 
            c[field.name] = v
294
 
        return struct.build(c)
295
 

	
296
 
    def __repr__(self):
297
 
        return "<%s %s>" % (self.__class__.__name__,
298
 
                            ', '.join(['%s: %s' % (field.name, _value_str(field._value)) for field in self._fields]))
299
 

	
300
 

	
301
 
if __name__ == '__main__':
302
 
    print('='*120)
303
 

	
304
 
    class TestFileRec(Record):
305
 
        nom = Field(str, 10, padchar='+', key=0)
306
 
        valeur = Int()
307
 
        numero = Int(size=2, key=1)
308
 
        k2 = Field(str, 5, key=2)
309
 
        k22 = Field(str, 6, key=2)
310
 
        x = Padding(4)
311
 

	
312
 
    print(dir(TestFileRec))
313
 
    print('name/V:', TestFileRec.numero.name)
314
 
    print('fields/T', TestFileRec._fields)
315
 
    print('keys', TestFileRec._keys)
316
 

	
317
 
    print('-'*120)
318
 

	
319
 
    ligne = TestFileRec(nom='toto', numero=1200)
320
 
    print('ligne:', ligne)
321
 
    print(dir(ligne))
322
 
    print('ligne.nom:', ligne.nom)
323
 
    ligne.nom = 'tata'
324
 
    print('ligne.nom:', ligne.nom)
325
 
    ligne.k22 = 'azerty'
326
 
    print('ligne.nom/v:', ligne.nom, ligne.valeur)
327
 
    print('ligne.key0', ligne._key2)
328
 
    print('ligne._struct', ligne._struct)
329
 
    x = ligne.rms_rec
330
 
    print([ord(c) for c in x])
0 comments (0 inline, 0 general)