[Info-vax] VMS Python IndexedFile - NotImplementedError: IndexedFile.primary_keynum

Arne Vajhøj arne at vajhoej.dk
Sat Sep 3 21:22:52 EDT 2022


On 8/30/2022 7:46 PM, Arne Vajhøj wrote:
> On 8/30/2022 1:40 PM, Simon Clubley wrote:
>> On 2022-08-28, Arne Vajhøj <arne at vajhoej.dk> wrote:
>>> So you create a class that inherits from IndexedFile and overrides
>>> primary_keynum?
>>>
>>> I will have to try that.
>>
>> For the benefit of anyone searching for this in the future, what
>> was the solution ?
> 
> That solved the problem in the subject line.
> 
> But then I had another problem. Jan-Erik emailed me a
> working example (thanks) and based on that I got it working.
> 
> Demo code:
> 
> from construct import *
> from vms.rms.IndexedFile import IndexedFile
> 
> class MsgRec:
>      def __init__(self, id, msg):
>          self.id = id
>          self.msg = msg
> 
> class MsgFil(IndexedFile):
>      def __init__(self, fnm):
>          IndexedFile.__init__(self, fnm, Struct('msgrec', SNInt32('id'), 
> String('msg', 100, padchar=' ')))
>      def primary_keynum(self):
>          return 0
> 
> db = MsgFil('msgs.isq')
> db.put(MsgRec(5, 'Hi from Python!'))
> for msg in db:
>      print(msg.msg)

And I got into more problems that I had to solve. Specifically
about VAX floating point and Cobol packed decimals in the
records.

Here is my solution in case anyone need to do something
similar.

Record structure is defined in Cobol as:

ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
     SELECT OPTIONAL D-FILE ASSIGN TO "TESTDB.ISQ" ORGANIZATION IS 
INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS D-ID.

DATA DIVISION.
FILE SECTION.
FD D-FILE.
01 D-RECORD.
     03 D-ID PIC X(8).
     03 D-IV PIC 9(8) COMP.
     03 D-ZV PIC 9(5)V9(2) PACKED-DECIMAL.
     03 D-XV COMP-2.
     03 D-SLEN PIC 9(4) COMP.
     03 D-SV PIC X(50).

Compiled with /FLOAT=G_FLOAT.

To get that working with Python:

from construct import *
from vms.rms.IndexedFile import IndexedFile
from decimal import *

from extras import *

class Data:
     def __init__(self, id, iv, zv, xv, sv):
         self.id = id
         self.iv = iv
         self.zv = zv
         self.xv = xv
         self.slen = len(sv.rstrip())
         self.sv = sv.ljust(50)

class DataFil(IndexedFile):
     def __init__(self, fnm):
         IndexedFile.__init__(self, fnm, Struct('data', String('id', 8), 
SNInt32('iv'), PackedDecimal('zv', 5, 2), VAXGFloat('xv'), 
UNInt16('slen'), String('sv', 50)))
     def primary_keynum(self):
         return 0
     def pack_key(self, keynum, keyval):
         return keyval
     def keyval(self, rec, keynum):
         return rec.id

Where extras.py contain some extra stuff that I needed to add
(building on the JFP stuff that VMS Python comes with). Copy
attached below.

Arne

====

from ctypes import CDLL, c_double, c_char_p, cast, byref
from construct import Adapter, Bytes
from vms.rms import BCD2Tuple, Tuple2BCD
from decimal import Decimal

CVT_K_VAX_F = 0
CVT_K_VAX_D = 1
CVT_K_VAX_G = 2
CVT_K_VAX_H = 3
CVT_K_IEEE_S = 4
CVT_K_IEEE_T = 5
CVT_K_IBM_LONG = 6
CVT_K_IBM_SHORT = 7
CVT_K_CRAY = 8

librtl = CDLL('LIBRTL.EXE')
lib_convert_float = getattr(librtl, 'CVT$CONVERT_FLOAT')

def float_to_python(val, typ):
     res = c_double()
     lib_convert_float(cast(val, c_char_p), typ, byref(res), 
CVT_K_IEEE_T, 0)
     return res.value

def float_from_python(val, typ):
     res = '12345678'
     lib_convert_float(byref(c_double(val)), CVT_K_IEEE_T, cast(res, 
c_char_p), typ, 0)
     return res

class VAXFFloatAdapter(Adapter):
     def _decode(self, obj, context):
         return float_to_python(obj, CVT_K_VAX_F)
     def _encode(self, obj, context):
         return float_from_python(obj, CVT_K_VAX_F)

def VAXFFloat(name):
     return VAXFFloatAdapter(Bytes(name, 4))

class VAXDFloatAdapter(Adapter):
     def _decode(self, obj, context):
         return float_to_python(obj, CVT_K_VAX_D)
     def _encode(self, obj, context):
         return float_from_python(obj, CVT_K_VAX_D)

def VAXDFloat(name):
     return VAXDFloatAdapter(Bytes(name, 8))

class VAXGFloatAdapter(Adapter):
     def _decode(self, obj, context):
         return float_to_python(obj, CVT_K_VAX_G)
     def _encode(self, obj, context):
         return float_from_python(obj, CVT_K_VAX_G)

def VAXGFloat(name):
     return VAXGFloatAdapter(Bytes(name, 8))

class PackedDecimalAdapter(Adapter):
     def __init__(self, byts, befdec, aftdec):
         Adapter.__init__(self, byts)
         self.befdec = befdec
         self.aftdec = aftdec
     def _decode(self, obj, context):
         return Decimal(BCD2Tuple(obj, self.aftdec))
     def _encode(self, obj, context):
         return Tuple2BCD(obj.as_tuple(), self.befdec, self.aftdec)

def PackedDecimal(name, befdec, aftdec):
     return PackedDecimalAdapter(Bytes(name, (befdec + aftdec + 2) / 2), 
befdec, aftdec)







More information about the Info-vax mailing list